<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7451330209409602076</id><updated>2012-01-20T20:35:02.372-08:00</updated><category term='See'/><category term='Think'/><category term='Collect'/><category term='Speak'/><category term='Hear'/><category term='Play'/><title type='text'>Half Hour Hacks</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default?start-index=101&amp;max-results=100'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>110</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5213268440737114277</id><published>2012-01-20T20:24:00.001-08:00</published><updated>2012-01-20T20:35:02.386-08:00</updated><title type='text'>Periodic Table (updated)</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://neednom.com/bfisher/blog/periodic/periodic2.html"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 66px;" src="http://2.bp.blogspot.com/-uiOcsMVb-9Q/Txo-RpyTn-I/AAAAAAAAAkk/lmsydkdvBwg/s320/ch.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5699936751386337250" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
I wrote this in 2005. Back then, Internet Explorer was the only browser that could render text upside down without a browser plugin. A few years later, other browsers added their own extensions like -webkit-transform. Today I updated this old html page for cross-browser support.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/periodic/periodic2.html"&gt;Try it!&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5213268440737114277?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5213268440737114277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5213268440737114277' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5213268440737114277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5213268440737114277'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2012/01/periodic-table-updated.html' title='Periodic Table (updated)'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-uiOcsMVb-9Q/Txo-RpyTn-I/AAAAAAAAAkk/lmsydkdvBwg/s72-c/ch.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4995436139054245227</id><published>2012-01-15T13:14:00.000-08:00</published><updated>2012-01-15T22:58:06.508-08:00</updated><title type='text'>SSIP</title><content type='html'>This tool will process source code and generates an index of terms. After the index is built, searches can be run very quickly, even on a large codebase. It is intended for c and c++ code, but has partial support for other languages like java and c#. The index is only for whole-string matching.
&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
First, make a copy of ssipexample.cfg and name it ssip.cfg. Edit the file so that it points to a directory containing source code.
&lt;pre&gt;
[main]  srcdir1=C:\path\to\my\source
&lt;/pre&gt;
Then, from a command line, one can do the following:
Type sssip –s main, and you should see a result for your main() function.
Full options are:
&lt;pre&gt;
sssip -s word  Search for a word
sssip -start  Re-build index
sssip -noindex word  Search for a word without using index
sssip -noindexnowhole word  Search for a partial word without using index
&lt;/pre&gt;

If you update one of your source files, the index will notice the change and keep itself up to date.
&lt;br/&gt;&lt;br/&gt;
This program is designed for speed, so it should be able to make a complete index quickly.

&lt;h3&gt;Download&lt;/h3&gt;
&lt;a href="http://neednom.com/hhhblog/ssip/simple_source_index.zip"&gt;Source and win32 binary&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/hhhblog/ssip/ssip_src.tar.gz"&gt;Source only&lt;/a&gt;
&lt;h3&gt;Measurements&lt;/h3&gt;
I've claimed this is 'fast'. What do I mean?
I downloaded the source for the Audacity audio editor, and the entire source for php 5.3.9. My test machine is a 2.67 Ghz machine I bought a year and a half ago. The test involves creating a db and indexing every file.
&lt;pre style="color:rgb(0,0,0);background-color:rgb(255,255,255)"&gt;
Test: audacity-src-1.2.6
987 source files.
9.351Mb of data indexed.
Execution time: 3.753s
Speed: 263 files/s

Test: php-5.3.9
1584 source files.
41.677Mb of data indexed.
Execution time: 6.297s
Speed: 251 files/s
&lt;/pre&gt;

Looks pretty good to me. :)

&lt;h3&gt;Description&lt;/h3&gt;

I use IDEs like Eclipse and Visual Studio for debugging, but find them far too bulky for everyday programming. They are slow to open and less customizable than my code editor of choice, SciTE. However, IDEs do have some useful features, in particular Go to Definition (which takes the currently-selected symbol and opens the file and line on which it is defined). SSIP is my first step in adding features like this to code editors like SciTE.
&lt;br/&gt;&lt;br/&gt;
My primary goal is speed. I want this to be usable on large codebases. I want even the initial run, creating the index from scratch, to take less than 10 seconds. Otherwise, there’s no point in generating an index, as a full-file search would be sufficient. I also found it enjoyable to optimize and tune a program, eschewing all C++ features (classes, exceptions) in favor of fast, raw, procedural code.
&lt;br/&gt;&lt;br/&gt;
I struck upon a way to quickly create an index that actually does not store any text at all. I used what is known as a hash function to map every word into a number. The function always maps the same word to the same number. For example, the word ‘hello’ maps to the number 1634674. The word ‘hello2’, though, maps to a completely different number, 26453545. When the user searches for the term ‘hello’, I use the same hash function to find its corresponding number and search only for the number.
This may seem too good to be true: I’ve transformed the 6 byte string ‘hello2’ into a 4 byte integer. The catch is that several inputs can map to the same number.  ‘zsesdvf’, ‘rtyrqtrdg’, and ‘hello2’ could all map to the same number, 26453545. This is perfectly fine for my search index, though, because before displaying the results to the user, I’ll do a second check to make sure that the result matches what the user typed. In other words, this type of search could return false positives, but the false positives can be easily filtered out, and in the end the results are always correct.
&lt;br/&gt;&lt;br/&gt;
A drawback of this method is that only full words can be searched.
&lt;br/&gt;&lt;br/&gt;
The initial search creates a database from scratch. The name of each file encountered is also stored along with its last-modified time. Subsequent searches will traverse through the files again and check each file’s last-modified time. If the file is new, or its update time is more recent than what we have, we process the file and update the database.
 &lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;More Implementation Details&lt;/h3&gt;
I chose to use Sqlite to store data. By using prepared queries, the sql only had to be parsed once per session, leading to performance improvements. 
&lt;br/&gt;&lt;br/&gt;
I also adjusted the hash function so that it didn’t require the length of the string up front. This allowed me to stream each character into the hash, and avoid any second buffer of the current word. (In general, I’ve noticed that avoiding memory allocations and having fewer buffers can lead to significant gains in performance).
&lt;br/&gt;&lt;br/&gt;
At first, I wrote a threadpool system where a main thread would hand out files to be processed by the workers. This worked, but I was surprised to see that the number of worker threads barely affected the speed of execution. Unfortunately, writing to the database was significantly slower than reading from the text file, and since sqlite only allows one writer at a time, this was a clear bottleneck. It would have been possible to create separate databases to be later merged, but this was too much of a hack even for me. I returned to single threaded execution, which did simplify the code.
&lt;br/&gt;&lt;br/&gt;
At this point, the program was logging string hash, line number found, id, file id, and other flags for each word. Because the id was incrementing, I could search for two consecutive terms with a bit of sql. The other flags included whether the line was indented or not, and the surrounding characters. Surrounding characters could among other things distinguish a method implementation, like ::foo. The size of the database on disk was rather large, though.
&lt;br/&gt;&lt;br/&gt;
Because writing to the database was my bottleneck, if I could store less data, the program would be faster.
&lt;br/&gt;&lt;br/&gt;
I realized that my prior experience showed me that reading through a file was unexpectedly fast. So, I decided to not even store the line number in my index at all. Results saying ‘the string “hello” is somewhere in the file “foo.cpp”’ is good enough, because it takes hardly any time to run through foo.cpp to list the true results.
&lt;br/&gt;&lt;br/&gt;
This also meant that I only had to record a particular string once per file. I came up with a very effective trick to rapidly detect duplicates. I was already hashing each string to a number. I reduced the number of bits in the hash to 24, probably reasonable because most words in source code are short. (Remember that in my design collisions might cause false positives that take time to be filtered out, but won’t affect correctness). This means that the number of distinct strings I was tracking was 2 to the 24th power, or 16,777,216. I then allocated a chunk of memory, 16,777,216 bytes worth to be exact. For each file I cleared all of the memory to 0, and as each string appeared I would find its hash and ‘check off’ the corresponding place in memory. I could then quickly see if a string had already been encountered in a file. (I could have also done this at the bit level, but bit operations take time, and a 24-bit hash has been sufficient). One might worry that hash collisions could cause us to incorrectly skip over a word because its brother has already been added to the db for that file, but this is not the case, because I consistently use the same hash algorithm. When searching, the program must still look through the file, because that hash did occur in that file. 
&lt;br/&gt;&lt;br/&gt;
The next step is for me to write a Python wrapper around Ssip that lets me ask questions like ‘find the constructor of the CFoo class’, ‘find the implementation of the bar() method’, and ‘find the type of the baz global variable’. I’ll then plug this into my code editor and be well on my way to having my own lightweight productivity toolkit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4995436139054245227?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4995436139054245227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4995436139054245227' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4995436139054245227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4995436139054245227'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2012/01/ssip.html' title='SSIP'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3115708979681237635</id><published>2012-01-10T17:20:00.000-08:00</published><updated>2012-01-13T13:08:05.695-08:00</updated><title type='text'>Wavcut,  Find Silence</title><content type='html'>A few posts ago I wrote about wavcuthhh, the tool I wrote to split .wav files into separate tracks.
I recently added the feature to look for periods of silence within a .wav file, and to cut the file based on those points.
This might be useful for splitting up dj sets that consist of one huge mp3 file, or for isolating the 'hidden track' that some bands will include
in the final track of an album after a minute of silence.
&lt;br/&gt;&lt;br/&gt;

I use an amplitude threshold to specify what is 'silent'. The transition is not always clean, though, because songs can have a beat and the amplitude will change frequently. First, I made a large low pass filter 
and basically averaged the amplitude about every 0.2 seconds. To check the results, I could have written a GUI, or printed to a text file and plotted the data, but instead, went with a quick and dirty solution: writing data
into another .wav file and using Audacity itself to visualize data.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://3.bp.blogspot.com/-Pj6yoixtccw/Tw_oapmzpBI/AAAAAAAAAkQ/t_V9NjKL8bA/s1600/find.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 97px;" src="http://3.bp.blogspot.com/-Pj6yoixtccw/Tw_oapmzpBI/AAAAAAAAAkQ/t_V9NjKL8bA/s320/find.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5697027598189831186" /&gt;&lt;/a&gt;
The upper channel approximates the current amplitude after lpf, the lower channel indicates whether the amplitude is above or below the cutoff.
&lt;br/&gt;&lt;br/&gt;
In a few cases, this was not enough, and there was still bouncing. I don't think there's a general way to solve this problem, because the silence between tracks can be the same as the silence within a track. 
I came up with a heuristic that has worked well so far. I assume that the first song is fading out, and the second song is fading in. Both of these might have temporary dips where the amplitude goes below the threshold, but then rises again.
My algorithm goes from left to right. When encountering a drop below the threshold, I examine the next 10 seconds of audio. I look for the longest string of consecutive samples where the amplitude is below the threshold, and choose the cut location as the middle of this string.
This tends to work because the pause between tracks tends to be longer than the temporary drops below the threshold during a fade-in or fade-out.
&lt;br/&gt;&lt;br/&gt;
Here's what it looks like (these are ~800kb because of an example .wav):&lt;br/&gt;
&lt;a href="http://neednom.com/hhhblog/wavcuthhh2_src.zip"&gt;C# source&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/hhhblog/wavcuthhh2_win32.zip"&gt;Win32 binary&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3115708979681237635?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3115708979681237635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3115708979681237635' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3115708979681237635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3115708979681237635'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2012/01/wavcut-find-silence.html' title='Wavcut,  Find Silence'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-Pj6yoixtccw/Tw_oapmzpBI/AAAAAAAAAkQ/t_V9NjKL8bA/s72-c/find.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1157271587238476530</id><published>2012-01-08T22:13:00.000-08:00</published><updated>2012-01-08T23:05:21.019-08:00</updated><title type='text'>Clipcircle32</title><content type='html'>Clipcircle is a Windows tool that lets you store multiple values in the clipboard at the same time and cycle between them.
&lt;br/&gt;&lt;br/&gt;

&lt;h3&gt;Example&lt;/h3&gt;
Run clipcircle32, and it will appear in the system tray.&lt;br/&gt;&lt;br/&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;width: 199px; height: 37px;" src="http://4.bp.blogspot.com/-M3L8pq-NE1U/TwqHxLt3ZTI/AAAAAAAAAkE/GO3RwyXBltM/s320/tray.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5695513957791917362" /&gt;
Copy a word of text from any program.&lt;/br&gt;
Copy a different word of text.&lt;br/&gt;
Open notepad&lt;br/&gt;
Press Windows-V. (The windows key has a flag icon and is typically between Ctrl and Alt)&lt;br/&gt;
The word you copied appears, but is selected!&lt;br/&gt;
Press Windows-V again.&lt;br/&gt;
Now, the first word that was copied appears.&lt;br/&gt;&lt;br/&gt;

Because Clipcircle selects as well as pastes, you can quickly cycle through the contents of the circle. By default, Clipcircle stores 8 items. It intentionally only stores this many items, so that you can repeatedly press
Windows-V to cycle through everything that is stored.&lt;br/&gt;&lt;br/&gt;

To close Clipcircle, press Windows-Escape.&lt;/br&gt;&lt;br/&gt;
&lt;h3&gt;Additional notes&lt;/h3&gt;
The idea comes from earlier versions of Visual Studio, where pressing Ctrl+Shift+V would also go through clipboard history, but without cycling. Clipcircle, though, works with any app. I'm aware that there are other clipboard tools, but this one has my full trust. For frequently typed strings that I want to always keep around, I use the excellent &lt;a href="http://utilfr42.free.fr/util/Clavier.php?sLang=en"&gt;Clavier+&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;

To customize how many items are stored, edit clipcircle.h, set g_nItems to another value, and re-build.
&lt;br/&gt;&lt;br/&gt;
It's lightweight: 9Kb on disk, and only 600Kb of private working set (memory usage).
&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;Download&lt;/h3&gt;
If you think this might be useful, feel free to give it a whirl. I've found it to be very helpful.&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/hhhblog/clipcircle.zip"&gt;Download&lt;/a&gt;, just unzip and run clipcircle32.&lt;br/&gt;
&lt;a href="http://neednom.com/hhhblog/clipcircle_src.zip"&gt;Source&lt;/a&gt; , GPLv3.&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1157271587238476530?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1157271587238476530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1157271587238476530' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1157271587238476530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1157271587238476530'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2012/01/clipcircle32.html' title='Clipcircle32'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-M3L8pq-NE1U/TwqHxLt3ZTI/AAAAAAAAAkE/GO3RwyXBltM/s72-c/tray.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5540546048697406457</id><published>2011-12-23T21:06:00.000-08:00</published><updated>2011-12-23T21:09:22.703-08:00</updated><title type='text'>wavcuthhh</title><content type='html'>I just wrote a little something to cut .wav files into smaller audio files. It doesn't load the whole file 
into memory, and so will support large files. Many other programs do this, even ffmpeg, but
I wanted to write this myself, in part because I wanted to be able to specify the cutpoints very precisely
(by sample number, in fact). I also wanted to be sure no audio data was lost or altered.
&lt;br/&gt;&lt;br/&gt;
To confirm correct results, I wrote a simple .wav analyzer that prints information about a wav file.
It confirms that information in the header is correct and outputs the riff chunks. Use the -check option to see this.
&lt;br/&gt;&lt;br/&gt;
This is called WavcutHHH so that it's clearly better than any existing program called Wavcut.
&lt;br/&gt;&lt;br/&gt;
&lt;pre&gt;
WavcutHHH will split a large .wav file into several files based on a text file
that specifies cut points.

wavcut.exe input.wav input.txt

First line of the .txt file is 'wavcut'.
Remaining lines of the .txt file should be numbers, specifying where to cut.
Refer to wavcut.exe trombone.wav trombone.txt as an example.

To check wav contents, use

wavcut.exe -check input.wav
&lt;/pre&gt;
&lt;br/&gt;&lt;br/&gt;
Here's what it looks like (these are ~800kb because of an example .wav):&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/11audio/wavcuthhh_src.zip"&gt;C# source&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/11audio/wavcuthhh_win32.zip"&gt;Win32 binary&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
On an unrelated note, I'm still in the midst of reorganizing my music library and moving to mp4.
There are more hacks in the pipeline.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5540546048697406457?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5540546048697406457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5540546048697406457' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5540546048697406457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5540546048697406457'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/12/wavcuthhh.html' title='wavcuthhh'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5746116016241891177</id><published>2011-10-18T22:55:00.000-07:00</published><updated>2011-10-18T23:08:49.612-07:00</updated><title type='text'>Mandelbrot Set on a FPGA</title><content type='html'>In April 2009, I developed an original system to compute and display the Mandelbrot set on FPGA hardware.
I used the capabilities of this platform to achieve speeds of more than 25 million results computed per second.

&lt;br/&gt;&lt;br/&gt;&lt;iframe src="http://player.vimeo.com/video/30658473?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="400" height="300" frameborder="0" webkitAllowFullScreen allowFullScreen&gt;&lt;/iframe&gt;

&lt;br/&gt;&lt;br/&gt;The FPGA, or field-programmable gate array, essentially consists of a large number of logic gates that can be configured to perform computation. Developing for this platform feels foreign at first. There is no operating system, or even a CPU.
Consequently, one cannot in general use a simple model where execution steps from one instruction to the next. One 
instead uses the fabric spatially, setting up associations and connections between components and placing logic in parallel. Making use of parallelism can lead to excellent performance solving certain problems.

&lt;br/&gt;&lt;br/&gt;I created this system on my own in my spare time, outside of class. I thank Professor Mark Chang 
for providing equipment and some advice.

&lt;br/&gt;&lt;br/&gt;&lt;h1&gt;Design&lt;/h1&gt;
My goal was to use the unique advantages of embedded programming to make a system that can perform computations very quickly.

&lt;br/&gt;&lt;br/&gt;I chose to compute the Mandelbrot set (yes, the cliché image) on the &lt;a href="http://en.wikipedia.org/wiki/Field-programmable_gate_array"&gt;FPGA&lt;/a&gt; platform, solving the problem of whether a particular x,y value was inside this set, to a given tolerance. My reasoning:
&lt;ul&gt;
&lt;li&gt;Nature of problem lends itself to parallelization&lt;/li&gt;
&lt;li&gt;Problem involves arithmetic, has opportunities for coming up with optimization strategies.&lt;/li&gt;
&lt;li&gt;Easy to visually verify correctness (and looks cool)&lt;/li&gt;
&lt;/ul&gt;

&lt;br/&gt;The configuration made by my design can only perform one task. A CPU, like the processor on the computer you are currently using, is general purpose,
and reads from a list of instructions to perform. If given a new list of instructions, it is able to execute them. The 'program' I describe here, on the other hand, has no such list of instructions, it is physically only capable of this one task.
&lt;br/&gt;&lt;br/&gt;
Like a processor, though, everything is still governed by a clock signal. In a more advanced system, the developer will 
divide the problem into different smaller operations that can be done per clock tick. Then, resource-expensive steps can be apportioned out (there are a limited number of hardware multipliers and other units available on the chip), and the overall 
clock speed can be set higher. In the design described here, though, I used a simpler approach where the same operations are done
on every clock tick. While this means the system clock speed might have to be slower, it still means that during each tick of the clock,
several multiplications and additions and memory reads are done at the same time -- very different than what occurs in a CPU.

&lt;br/&gt;&lt;br/&gt;I used four types of components to solve the problem. A 'Child' unit receives x,y coordinates and screen coordinates as input. It has an internal state machine.
In `State_calc it performs one iteration of the Mandelbrot set computation. If the current value is too great (pixel is not in Mandelbrot set), or the number 
of iterations is sufficiently large (pixel is probably in Mandelbrot set),
state moves to `State_write which will write the result to memory by setting a write-enable flag. `State_acqnew retrieves the next value that should be computed
and resets computation. Each child had a separate RAM unit so that it could independentally read and write to memory.
The 'Parent' unit contains several Child units and is responsible for dividing work among them. The current set of uncomputed values is given to the first child that is not busy.
The ColorMap unit maps the number of iterations to an R,G,B color, using some shifting to unpack colors from distributed ROM. Because this isn't an art project, I just reverse-engineered and used colors from the shareware program UltraFractal.
The VGA unit reads a value from memory, translates it to a color with the ColorMap unit, and sets output voltage on a physical pin. VGA scans through the image more quickly than values can be computed, which is why separate memory is required.

&lt;br/&gt;&lt;br/&gt;I could then connect these units. (I find it interesting that when on hardware, the units are literally physically connected to each other. Makes one realize that so much programming one does is very far from the hardware.)
The design was intentionally flexible with regard to the number of Child units. This number can be increased until reaching the capacity of the fabric, so that my design will easily scale if a FPGA with more gates is available.

&lt;br/&gt;&lt;br/&gt;&lt;h1&gt;Simulation&lt;/h1&gt;
I first made a Verilog module called vga_simulator that could be used in ModelSim. This module received r, g, and b values with the same interface as an actual
vga output. It wrote to a file on disk that could later be transcribed into a standard bitmap image.

&lt;br/&gt;&lt;br/&gt;I then wrote a C implementation of the Child algorithm. Because this prototype ran quickly, I could use it 
to experiment with the color table, number of iterations, and initial view.

&lt;br/&gt;&lt;br/&gt;I translated my design to Verilog, using the vga_simulator to test results. I used fixed point arithmetic, which was sufficient for the problem. The product of x and y is written (x*y)&amp;gt;&amp;gt;&amp;gt;`FIXPTSIZE. 
Floating point would not have been practical for the scope of my project. I started with just one Child, and black and white, to reduce possible points of error, before slowly re-adding functionality.
One obstacle was that I needed to use both signed reg and signed shift (&amp;gt;&amp;gt;&amp;gt;) in a certain part of my arithmetic. Coming from C, I had not realized that I needed separate syntax for signed shift, and so it took some time to realize was incorrect.&lt;br/&gt;&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-bfE-e0FqhQE/Tp5mp05GRcI/AAAAAAAAAiw/2ELiplh7EPQ/s1600/figbrkout.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 234px;" src="http://2.bp.blogspot.com/-bfE-e0FqhQE/Tp5mp05GRcI/AAAAAAAAAiw/2ELiplh7EPQ/s320/figbrkout.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5665078250037462466" /&gt;&lt;/a&gt;

&lt;br/&gt;
(This picture shows the result of a bug in my scheduler. Sometimes it is broken code that makes the coolest-looking results.)

&lt;br/&gt;&lt;br/&gt;With a working simulation, and a highly-visible counter that showed how many clock ticks were necessary to complete computation, I could work on optimization.

&lt;br/&gt;&lt;br/&gt;&lt;h1&gt;Optimization&lt;/h1&gt;
Optimizing for this platform was a different experience than optimizing typical software. As well as taking less clock ticks to complete, the program also has to be optimized in space, to reduce the number of gates needed.
This led to behavior that I did not expect:
In typical software optimization, I will often check for conditions so that I can do less work if necessary, and skip going down an unneeded code path. In hardware, though, conditions are expensive, because each requires wiring.
"Skipping a code path" is sometimes meaningless - the entire path is done in one clock tick anyways, and it is taking up space, so it is often better to --always-- be performing the computation, and just ignore the result if it happened to not be needed.

&lt;br/&gt;&lt;br/&gt;
Straightforward optimizations included tuning constants so that iterations decreased while the image quality is still acceptable. I set up initial conditions and coordinates to values that the Parent scheduler happened to work more efficiently with. I tightened up the state machine in the child module, coming up with a way to need only two states. I rewrote the scheduler so that it did not need a barrel-shifter. For the sum of two fixed-point products, I could right-shift the sum instead of the individual products. I moved most logic to assign= statements.

&lt;br/&gt;&lt;br/&gt;I could save significant time by skipping computation inside the inner 'cardioid' shape. I soon learned, though, that multiplication was expensive due the design I chose. There were limited hardware multipliers, and 
I needed all children to perform at least 3 multiplies every clock tick. On the real hardware I had, there was no room for this shortcut.

&lt;br/&gt;&lt;br/&gt;Multiplication by a constant, though, I realized could be done very quickly. It was fun to re-write multiplication by 320, as n*320 == (n&amp;lt;&amp;lt;6) + (n&amp;lt;&amp;lt;8). Division by powers of 2, of course, I also converted into shifts.
I aggressively re-used computed values, such as how x2 and y2 could be used in both iteration and checking magnitude.
One attempt was to use an "approximation" abs(x)+abs(y) instead of the true magnitude, and to replace x2-y2 with (x+y)*(x-y), saving one multiply. This changed the 
visual appearance in an odd way, though. &lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-9THXp9PbPbg/Tp5muzf2LkI/AAAAAAAAAi8/Y6uwCtUuuYY/s1600/coolbail-Copy%2Bof%2Bvgaout.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/-9THXp9PbPbg/Tp5muzf2LkI/AAAAAAAAAi8/Y6uwCtUuuYY/s320/coolbail-Copy%2Bof%2Bvgaout.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5665078335562460738" /&gt;&lt;/a&gt;

&lt;br/&gt;I was pleasantly surprised to see in the logs that the Xilinx software understood that the Child module was using a state machine, it apparently could place the state reg in a more efficient way.

&lt;br/&gt;&lt;br/&gt;&lt;h1&gt;Implementation&lt;/h1&gt;
Developing with simulation can be misleading. You can use as much memory as you want, and it seems perfectly fine to multiply and even divide (!) numbers with just the symbols * and /.
On real hardware, RAM is limited, and in my case, hardware multipliers even more limited. One makes tradeoffs between longest chain of logic and clock speed.
Also, the software for synthesis has a learning curve, and synthesizing can take a significant amount of time.

&lt;br/&gt;&lt;br/&gt;I succeeded in synthesizing the project onto the hardware available to me, a Xilinx Spartan3 XC3s1500.

&lt;br/&gt;&lt;br/&gt;To make the image look interesting, I shifted the color palette over time, making the colors move.
The entire image is recomputed every time the colors shift, an intentional way of demonstrating how fast my system is.
As the video above shows, the results looked great, and the refresh rate was more than sufficient.

&lt;br/&gt;&lt;br/&gt;With the limitations of the specific hardware used, my final configuration was:
4 computing children
64 colors in palette
320x240 image
25Mhz clock speed
68,024 ticks to draw one frame at standard zoom

&lt;br/&gt;&lt;br/&gt;With a larger FPGA, I could have increased the number of children to drive these numbers higher. Even still, this math brought a smile to my face:
68,024 ticks to compute 76,800 pixels
68,024 ticks @ 25Mhz to compute 76,800 pixels
0.00272096 seconds to compute 76,800 pixels
1 second to compute 28,225,332 pixels

&lt;br/&gt;&lt;br/&gt;28 million computed values per second sounds pretty nice to me.

&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-h-joGUz1na0/Tp5mzVEICYI/AAAAAAAAAjI/p23nq4JR-eY/s1600/stat.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 192px;" src="http://2.bp.blogspot.com/-h-joGUz1na0/Tp5mzVEICYI/AAAAAAAAAjI/p23nq4JR-eY/s320/stat.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5665078413292472706" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5746116016241891177?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5746116016241891177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5746116016241891177' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5746116016241891177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5746116016241891177'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/10/mandelbrot-set-on-fpga.html' title='Mandelbrot Set on a FPGA'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-bfE-e0FqhQE/Tp5mp05GRcI/AAAAAAAAAiw/2ELiplh7EPQ/s72-c/figbrkout.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-8393301920385694181</id><published>2011-10-17T22:53:00.000-07:00</published><updated>2011-10-18T22:55:01.495-07:00</updated><title type='text'>Big Acoustic Fingerprints</title><content type='html'>A few days ago, I found myself in a situation that begged for automation. As Larry Wall says, for a software developer, 
laziness is a virtue (good programmers will learn or invent shortcuts to save time). I had recorded many .mov files from my digital camera,
and wanted to extract the audio track. I wanted to create one .mp3 file for each .mov file with its corresponding audio.

&lt;br/&gt;&lt;br/&gt;The shareware program &lt;a href="http://www.highcriteria.com/"&gt;Total Recorder&lt;/a&gt;, which I recommend, can record any audio played by 
your computer to a .wav or .mp3 file on disk. With this and Windows 7 Media Player's ability to play .mov files, I was already half-way to the solution.
I could start Total Recorder, put all of the .mov files in a playlist, and wait while the audio tracks were recorded in real-time.

&lt;br/&gt;&lt;br/&gt;The result, though, would be one large .mp3 file that would have to be cut manually into pieces. For dozens of tracks, this would be a tedious process.

&lt;br/&gt;&lt;br/&gt;My first thought was to write down the list of lengths, and use that list to cut the .mp3 file. I know that the first track is 1:21, the second is 1:06, and so on.
This approach, though, was prone to error. The times were rounded to the nearest second, and so by the end, errors would accumulate. Also, the Media Player itself
might add slight pauses between tracks.

&lt;br/&gt;&lt;br/&gt;I then realized that I could put a 'marker' sound between each track. After recording, I could look for the markers, and split the audio precisely.

&lt;br/&gt;&lt;br/&gt;One approach to do this would be to use a generated combination of tones. I could pick a certain number of pitches that would be rare to encounter in the real audio data,
and create a marker based on these frequencies. When processing afterwards, I would split the audio data into small fragments, run FFT on each fragment, and search
for fragments that contained just those frequencies (allowing some margin for error). This approach would be robust to mp3 audio compression, and even
playback on different speakers.

&lt;br/&gt;&lt;br/&gt;For this particular setup, though, I did not need to be as robust, because Total Recorder will record the audio digitally before it is converted to analog, although the signal is still slightly altered by 
mp3 compression and the operating system. As a half hour hack, I made an audio marker that has an unusual profile:&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-F9gBIEYmpkM/Tp5imVhIwTI/AAAAAAAAAiY/KL42MzoApL8/s1600/f1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 252px; height: 215px;" src="http://2.bp.blogspot.com/-F9gBIEYmpkM/Tp5imVhIwTI/AAAAAAAAAiY/KL42MzoApL8/s320/f1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5665073792029344050" /&gt;&lt;/a&gt;


&lt;br/&gt;Over the course of one second, the signal value is increasing. Playing this on speakers, the sound is silent except for a click at the end when the signal suddenly drops to 0. I left the click intentionally in order to hear the sound. This is very different than a typical audio signal, which fluctuates up and down rapidly; the quick fluctuations causing an
audible pitch:&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-x9EuNCnB3-w/Tp5ipPm2VRI/AAAAAAAAAik/7YHbxCP2dJk/s1600/f2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 249px; height: 226px;" src="http://3.bp.blogspot.com/-x9EuNCnB3-w/Tp5ipPm2VRI/AAAAAAAAAik/7YHbxCP2dJk/s320/f2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5665073841982297362" /&gt;&lt;/a&gt;

&lt;br/&gt;It was trivial for me to find areas in the audio data where the signal value increases for more than half a second. To make the system more robust, I looked at the averages of blocks of 16 samples, and looked for long sequences where these averages were always increasing. (Note that 
this block size can't be safely set to a large value, because a low sound increasing in volume with DC offset could trigger false positives).
After finding a match, I could skip forward several seconds, knowing that no track would be shorter than 10 seconds. It's a good thing I had my own audio library; the wav files were too big to realistically store in memory, but because I was familiar with reading raw .wav data I could just scan through the stream of bytes.
My program output the cut points in milliseconds to a plain text file, adjusting the times to trim away the audio marker.

&lt;br/&gt;&lt;br/&gt;The final workflow:&lt;br/&gt;
1) A python script creates a .wpl Windows media player playlist of the .mov files separated by my marker audio.
&lt;br/&gt;
2) Start Total Recorder and start the playlist, and wait for a while.
&lt;br/&gt;
3) Save the recorded audio as .wav and run my program to determine cut points.
&lt;br/&gt;
4) Encode as mp3.
&lt;br/&gt;
5) I use the excellent &lt;a href="http://mpesch3.de1.cc/mp3dc.html"&gt;mp3directcut&lt;/a&gt; to split the audio at those points. A python script turns my text file into a 
.cue file that mp3directcut uses to split the audio into pieces.

&lt;br/&gt;&lt;br/&gt;And that's all. The system worked very well and hit no false positives. mp3directcut can (magically?) slice up a mp3 file without recompressing, so the process is lossless and very quick.
I now have an automated way to cut a long mp3 file into pieces.

&lt;br/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-8393301920385694181?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/8393301920385694181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=8393301920385694181' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8393301920385694181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8393301920385694181'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/10/big-acoustic-fingerprints.html' title='Big Acoustic Fingerprints'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-F9gBIEYmpkM/Tp5imVhIwTI/AAAAAAAAAiY/KL42MzoApL8/s72-c/f1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-9123824725421584796</id><published>2011-09-22T22:22:00.001-07:00</published><updated>2011-09-22T23:45:58.457-07:00</updated><title type='text'>Beat Circles</title><content type='html'>Make beats. Tweak them by shifting each channel. Save and send to friends.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/h5audio/htmlfreq_beat/"&gt;&lt;img style="display:block;margin:0px auto 10px; text-align:center;width: 320px; height: 313px;" src="http://4.bp.blogspot.com/--S_MpyzyjOU/TnwqWz9OoZI/AAAAAAAAAiQ/R4YENN16EAg/s320/clhere.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5655441803463008658" /&gt;&lt;/a&gt;

If you have Firefox, &lt;a href="http://neednom.com/bfisher/blog/h5audio/htmlfreq_beat/"&gt;try it&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
If you don't have Firefox, it looks a bit like the image above. My novel layout shows the repeating loop as an actual circular loop.
&lt;br/&gt;&lt;br/&gt;
Some demo beats I came up with (but you really should put together your own):
&lt;br/&gt;&lt;br/&gt;
&lt;object height="81" width="100%"&gt; &lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F23968042"&gt;&lt;/param&gt; &lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F23968042" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;
&lt;br/&gt;&lt;br/&gt;
Use the icons on the left for more adjustments:
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://2.bp.blogspot.com/-a--8shQhqKU/Tnwavz4wU8I/AAAAAAAAAiI/zWjB30wUMPM/s1600/eye.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://2.bp.blogspot.com/-a--8shQhqKU/Tnwavz4wU8I/AAAAAAAAAiI/zWjB30wUMPM/s320/eye.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5655424640754930626" /&gt;&lt;/a&gt;
Load a demo.
&lt;a href="http://1.bp.blogspot.com/-qeFNx0opkIE/Tnwas5NDspI/AAAAAAAAAiA/e0JvTNDIaO4/s1600/chart_line_add.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://1.bp.blogspot.com/-qeFNx0opkIE/Tnwas5NDspI/AAAAAAAAAiA/e0JvTNDIaO4/s320/chart_line_add.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5655424590642655890" /&gt;&lt;/a&gt;
Change the number of divisions, so that you can make the 7/4 beat you always wanted.

&lt;a href="http://4.bp.blogspot.com/-4motuSyYTm4/TnwakgDHwzI/AAAAAAAAAh4/M0RukT2F_b8/s1600/layers.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://4.bp.blogspot.com/-4motuSyYTm4/TnwakgDHwzI/AAAAAAAAAh4/M0RukT2F_b8/s320/layers.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5655424446451139378" /&gt;&lt;/a&gt;
A shortcut: after it is clicked, when you create a drum-hit in the upper-right quadrant, the corresponding 
drum-hit is also turned on in the other three quadrants.
&lt;br/&gt;&lt;br/&gt;
Because the sound is generated in real time, you can edit the rhythm, and even tempo, during playback. If the volume of a channel is very high, I 'distort' the sample by intentionally causing clipping.
&lt;br/&gt;&lt;br/&gt;
I made this for fun. Real software is more complicated.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-9123824725421584796?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/9123824725421584796/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=9123824725421584796' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/9123824725421584796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/9123824725421584796'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/09/beat-circles.html' title='Beat Circles'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/--S_MpyzyjOU/TnwqWz9OoZI/AAAAAAAAAiQ/R4YENN16EAg/s72-c/clhere.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2427073314557254342</id><published>2011-09-15T21:49:00.000-07:00</published><updated>2011-09-22T23:13:00.359-07:00</updated><title type='text'>Laser Sounds</title><content type='html'>It's another real-time audio experiment, this one even less serious. Click the 'play' button and drag the dot across the screen
to make "laser sounds".
&lt;br/&gt;&lt;br/&gt;

&lt;a href="http://neednom.com/bfisher/blog/h5audio/htmlfreq_lasers/"&gt;Try it&lt;/a&gt;! (in a recent version of Firefox).
&lt;br/&gt;&lt;br/&gt;
An example - but there are other sounds to be made that I haven't discovered yet.&lt;br/&gt;&lt;br/&gt;
&lt;object height="81" width="100%"&gt; &lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F23967173"&gt;&lt;/param&gt; &lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F23967173" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;
&lt;br/&gt;&lt;br/&gt;
Moving left to right will increase frequency, moving downwards will increase harmonic content. Moving slowly to the left
with a square wave can make 90s arcade-game like noises. Increase the 'sparkle' to create even older-sounding arcade game
noises.
&lt;br/&gt;&lt;br/&gt;
What's going on? I am playing one tone and quickly changing the frequency. When the frequency is changed slowly, one hears
a typical siren like one would expect. If the frequency is changed more quickly, the result is a "tshu" pulse sound. Finally, if the frequency is
changed very quickly, the entire quality of the sound can change. This is called FM synthesis, where FM stands for frequency modulation. Radio transmitters
use Frequency Modulation, changing the pitch of a single note very quickly, to 'move' a signal in frequency to a very high frequency.
Video games in the 90s also used FM synthesis; when putting this program in square wave mode and moving to the left, the sound resembles
that of a DOS game or GameBoy handheld. Maybe I'll follow the work of some musicians (Bibio, Crystal Castles) in incorporating arcade-like noises into music.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2427073314557254342?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2427073314557254342/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2427073314557254342' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2427073314557254342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2427073314557254342'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/09/laser-sounds.html' title='Laser Sounds'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4356569534520914760</id><published>2011-09-02T23:37:00.000-07:00</published><updated>2011-09-02T23:46:49.339-07:00</updated><title type='text'>Tremolo Machine</title><content type='html'>If you have Firefox: this is a machine that makes sounds. It's not a very serious project, but I find it entertaining. 
Click and drag the dots in order to move the levers, and click the play button to hear the result.
(Further changes are not picked up in real time; you have to click play again.)
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-MDtTdOt_sXk/TmHMJR-ZpiI/AAAAAAAAAhg/wSFxmrcVabg/s1600/tremolo.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 170px;" src="http://2.bp.blogspot.com/-MDtTdOt_sXk/TmHMJR-ZpiI/AAAAAAAAAhg/wSFxmrcVabg/s320/tremolo.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5648019867515332130" /&gt;&lt;/a&gt;
&lt;br/&gt;
Try this:
&lt;br/&gt;&lt;br/&gt;

&lt;a target="_new" href="http://neednom.com/bfisher/blog/h5audio/htmlfreq_tremolo/index.html?{v:1,d:{channels:[{FF:0.175,FF_a:240.3585939225755,waveTT:square,waveTT_f:2.8499999999999996,vol:0.07750000000000001,modifiers:[{modTT:vibc,modTT_f:5.699999999999999,FF:0.9283333333333333,FF_a:6.904422222222222,width:0.9750000000000001,phase:1},{modTT:vibc,modTT_f:5.3,FF:0.40166666666666667,FF_a:1.300688888888889,width:0.8999999999999999,phase:0.3},{modTT:off,modTT_f:0,FF:0.035000000000000024,FF_a:0.019800000000000012,width:0.1,phase:0.74},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]},{FF:0.125,FF_a:176.45477367468987,waveTT:square,waveTT_f:2.4000000000000004,vol:0.05,modifiers:[{modTT:vibc,modTT_f:5.8999999999999995,FF:0.8516666666666666,FF_a:5.8126888888888875,width:1.5,phase:1},{modTT:vibc,modTT_f:5.2,FF:0.4683333333333332,FF_a:1.764688888888888,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]},{FF:0.3,FF_a:414.8222889673691,waveTT:off,waveTT_f:0,vol:0.05,modifiers:[{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]}]}}"&gt;
Bubble music&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;

&lt;a target="_new" href="http://neednom.com/bfisher/blog/h5audio/htmlfreq_tremolo/index.html?{v:1,d:{channels:[{FF:0.125,FF_a:176.45477367468987,waveTT:saw,waveTT_f:3.6499999999999995,vol:0.08583333333333333,modifiers:[{modTT:vib,modTT_f:0.2,FF:0.32833333333333337,FF_a:0.8724222222222224,width:0.35,phase:0.54},{modTT:vib,modTT_f:1.9,FF:0.16833333333333336,FF_a:0.23668888888888898,width:0.8999999999999999,phase:1},{modTT:vib,modTT_f:1.6,FF:0.23500000000000001,FF_a:0.4518000000000001,width:0.4,phase:1},{modTT:vib,modTT_f:1.7,FF:0.13500000000000004,FF_a:0.15580000000000008,width:0.8250000000000001,phase:1},{modTT:RTEN,modTT_f:4.3999999999999995,FF:0.6933333333333334,FF_a:3.855688888888889,width:0.1,phase:1}]},{FF:0.3,FF_a:414.8222889673691,waveTT:off,waveTT_f:0,vol:0.05,modifiers:[{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]},{FF:0.3,FF_a:414.8222889673691,waveTT:off,waveTT_f:0,vol:0.05,modifiers:[{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]}]}}"&gt;
Curious drill
&lt;br/&gt;&lt;br/&gt;

&lt;a target="_new" href="http://neednom.com/bfisher/blog/h5audio/htmlfreq_tremolo/index.html?{v:1,d:{channels:[{FF:0.275,FF_a:378.16347678908375,waveTT:sine,waveTT_f:0.2,vol:0.014166666666666668,modifiers:[{modTT:trem,modTT_f:2.6,FF:0.32,FF_a:0.8292,width:1.7249999999999999,phase:0.6},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]},{FF:0.15,FF_a:208.00730572280813,waveTT:saw,waveTT_f:3.5999999999999996,vol:0.017499999999999998,modifiers:[{modTT:vib,modTT_f:1.7,FF:0.01,FF_a:0.0108,width:0.15000000000000002,phase:1},{modTT:RTEN,modTT_f:4,FF:0.2849999999999999,FF_a:0.6597999999999996,width:1.5,phase:1},{modTT:vib,modTT_f:1.0999999999999999,FF:0.07666666666666658,FF_a:0.05702222222222211,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]},{FF:0.3,FF_a:414.8222889673691,waveTT:noise,waveTT_f:5.2,vol:0.03416666666666667,modifiers:[{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1},{modTT:off,modTT_f:0,FF:0.01,FF_a:0.0108,width:1.5,phase:1}]}]}}"&gt;
Beach things&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;

Click the eye icon for more examples (be prepared to turn down the volume).
&lt;br/&gt;&lt;br/&gt;

'vib' is vibrato, causing the pitch to change pitch over time.&lt;br/&gt;
'trem' is tremolo, causing the sound to become quieter and louder over time.&lt;br/&gt;
'vib10' and 'trem10' are more potent versions of 'vib' and 'trem' (the frequency is 10 times faster). &lt;br/&gt;
trem10 with a high frequency can be an interesting effect. vib10 with a high frequency can in effect use frequency modulation to create another pitch. &lt;br/&gt;
vibc is the cooing of a bird; it plays one quarter of the vibrato and silences the rest.&lt;br/&gt;
&lt;br/&gt;
The smaller control to the right of the modification type can adjust phase.
&lt;br/&gt;&lt;br/&gt;
The button on the left, above the eye, will enable 'layer audio'. Now whenever you click play, you hear the previous results too, and can layer sounds upon sounds.

&lt;br/&gt;&lt;br/&gt;
To make fine adjustments, click the textfield button to manually enter a value. Alternatively, click save and make adjustments to the data in the url.
&lt;br/&gt;&lt;br/&gt;
&lt;a target="_new" href="http://neednom.com/bfisher/blog/h5audio/htmlfreq_tremolo/index.html"&gt;
Try it!&lt;/a&gt;
&lt;br&gt;(Firefox only)
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4356569534520914760?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4356569534520914760/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4356569534520914760' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4356569534520914760'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4356569534520914760'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/09/tremolo-machine.html' title='Tremolo Machine'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-MDtTdOt_sXk/TmHMJR-ZpiI/AAAAAAAAAhg/wSFxmrcVabg/s72-c/tremolo.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1416177473690042249</id><published>2011-08-06T22:57:00.000-07:00</published><updated>2011-08-06T23:55:31.213-07:00</updated><title type='text'>BookmarkBuckets</title><content type='html'>Over the years, I have accumulated a fair number of website URLs. In one form or another, I've kept links to music videos, webcomics, essays, computational complexity blogs, artwork, and anything else that I found to be worth saving. Some of these links I organized in a OneNote web app on SkyDrive (so that it could easily be accessed from many computers). However, I wanted my own system, so that I could easily include screenshots of each page (a crucial feature for me - even if the thumbnail is too small to see content, just the color scheme can sometimes trigger one's memory of the website).
&lt;br/&gt;
&lt;br/&gt;My requirements:
&lt;br/&gt;-Thumbnail screenshots of the webpage
&lt;br/&gt;-Easy to organize bookmarks into folders, not just tags
&lt;br/&gt;-Ability to export bookmarks and titles to simple plain-text
&lt;br/&gt;-Ability to sync bookmarks between machines
&lt;br/&gt;-Quickly navigate, rename, open, delete
&lt;br/&gt;
&lt;br/&gt;My first thought was to create some non-trivial web application, maybe with some new UI concepts to browse the data. I could use a service like &lt;a href="http://www.thumbalizr.com/"&gt;thumbalizr&lt;/a&gt; to get thumbnail.  However, as Larry Wall says, for a programmer, laziness is a virtue, and I didn't want to spend very much time on what would probably be a program only I cared about. (There being many existing apps like readitlater, etc).
&lt;br/&gt;
&lt;br/&gt;Then came the idea.
&lt;br/&gt;
&lt;br/&gt;I will use metadata embedded in a normal .jpg image as my file format, and organize the bookmarks as normal files in Windows Explorer! Each bookmark is represented by a .jpg image that is a screenshot of the website. The name of the file is the name of the site. The url is stored in exif metadata within the image, a standard format that can be read by many programs. Now 90% of the work is done for me - navigating, renaming, moving into folders. Syncing between machines can be done with Dropbox or Skydrive. Exporting to plain text can be done with a simple Python script. To open in a webpage, I drag the .jpg into the "bookmarkbuckets" window and the url will open in one's default browser.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-BxMlP2CCQY8/Tj4qQ80b6DI/AAAAAAAAAhQ/aGV-xhL8_2Q/s1600/bb.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://3.bp.blogspot.com/-BxMlP2CCQY8/Tj4qQ80b6DI/AAAAAAAAAhQ/aGV-xhL8_2Q/s200/bb.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5637990254206773298" /&gt;&lt;/a&gt;


&lt;br/&gt;1) Start with a plain text file with urls and titles on alternating lines, or a folder of .url files.
&lt;br/&gt;2) A short &lt;a href="http://code.google.com/p/launchorz/"&gt;lnzscript&lt;/a&gt; file runs which opens each url in a browser and takes a screenshot as a .jpg image.
&lt;br/&gt;3) Bookmark buckets modifies the .jpg image to add the URL in exif data. It also adds the title.
&lt;br/&gt;Now, these .jpg files are all are needed. One can rename and categorize as needed.
&lt;br/&gt;To open the corresponding webpage, drag the .jpg and drop it into the "bookmarkbuckets" window.
&lt;br/&gt;(Bookmark buckets can also export all links back to a tab-delimited file).
&lt;br/&gt;
&lt;br/&gt;It's worked well for me. I'll release to the public under a GPL license in the future.&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-FQOdIvXH6OU/Tj42wcih2ZI/AAAAAAAAAhY/iihgeAPKZYM/s1600/bb2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 162px;" src="http://3.bp.blogspot.com/-FQOdIvXH6OU/Tj42wcih2ZI/AAAAAAAAAhY/iihgeAPKZYM/s200/bb2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5638003989437077906" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;br/&gt;Postscript:
&lt;br/&gt;It seems to me that there are (or were) 3 usages of bookmarks:
&lt;br/&gt;1) a shortcut to a commonly accessed page
&lt;br/&gt;2) a way to remember a less-commonly accessed page
&lt;br/&gt;3) a way to put aside a webpage in order to return to it later
&lt;br/&gt;(I made this list in 2007)
&lt;br/&gt;I note that these have changed over the past few years:
&lt;br/&gt;Shortcuts like the awesome bar, and Google instant have all but eliminated the need for 1.
&lt;br/&gt;Apps like instapaper and readitlater address 3.
&lt;br/&gt;I'll try to make Bookmarkbuckets effective for 2.
&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1416177473690042249?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1416177473690042249/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1416177473690042249' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1416177473690042249'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1416177473690042249'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/08/bookmarkbuckets.html' title='BookmarkBuckets'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-BxMlP2CCQY8/Tj4qQ80b6DI/AAAAAAAAAhQ/aGV-xhL8_2Q/s72-c/bb.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1778003387652148306</id><published>2011-05-31T00:13:00.001-07:00</published><updated>2011-05-31T00:30:58.162-07:00</updated><title type='text'>Waveform UI</title><content type='html'>I'm having fun writing code for real-time audio effects.
&lt;br/&gt;&lt;br/&gt;
If you have Firefox 4, you can play with this &lt;a href="http://neednom.com/bfisher/blog/h5audio/waveform/"&gt;Waveform UI&lt;/a&gt; toy. Click the play button to start the audio, click and drag on the dots to change the sounds!
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://neednom.com/bfisher/blog/h5audio/waveform/"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 189px;" src="http://1.bp.blogspot.com/-8YHI2LyGEZ4/TeSWprpoeLI/AAAAAAAAAbs/TAV7rfGG3Fg/s400/blogp.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5612776678446626994" /&gt;&lt;/a&gt;
&lt;br/&gt;
You can experiment with what different wave shapes sound like. In general, smoother curves have less high-pitched buzzing, because there are fewer harmonics needed to approximate that curve. I haven't found very many interesting sounds in the time domain like this, though, so you'll have to wait for further experiments to make a variety of textures.
&lt;br/&gt;&lt;br/&gt;
The "cut" setting, if turned on, takes a part of the waveform and sets it to 0. (This isn't reflected in the waveform graphic.)
&lt;br/&gt;&lt;br/&gt;
I made the interface using raphaeljs, which I recommend. This project is made possible by Firefox 4's cool experimental raw audio data api. Thanks to those at Mozilla.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1778003387652148306?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1778003387652148306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1778003387652148306' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1778003387652148306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1778003387652148306'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/05/waveform-ui.html' title='Waveform UI'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-8YHI2LyGEZ4/TeSWprpoeLI/AAAAAAAAAbs/TAV7rfGG3Fg/s72-c/blogp.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-84030768438615881</id><published>2011-04-25T20:54:00.000-07:00</published><updated>2011-04-25T20:57:09.262-07:00</updated><title type='text'>Visualising musical appetite</title><content type='html'>Last night I thought of a way to plot my music library over time. I quickly wrote a python script to make a histogram of when songs were added. Because my music is already sorted by genre, I added that to the visualization.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-CWBD3BlOWlE/TbZCQk-LmyI/AAAAAAAAAbc/IKihyEaczEI/s1600/s1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 234px;" src="http://2.bp.blogspot.com/-CWBD3BlOWlE/TbZCQk-LmyI/AAAAAAAAAbc/IKihyEaczEI/s400/s1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5599736039251614498" /&gt;&lt;/a&gt;

I call this plot "musical appetite", because that conveys both taste and quantity.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://3.bp.blogspot.com/-T0drNPQrwhI/TbZCUsugwCI/AAAAAAAAAbk/KW80yqE8-7w/s1600/s2.PNG"&gt;Larger render&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;

I haven't really analyzed this, but see a few trends. Added a lot of rock in 5/2007. I think the upticks correspond with college spring and winter breaks :) Currently, nearly all growth is in 2000s and electronic. A project actually done in 30 minutes for a change.
&lt;br/&gt;&lt;br/&gt;
*The music was manually sorted, as opposed to using an algorithm, but genres do overlap. My 19 categories are here combined to 11. To clarify, the category rock includes rock music from the 60s and 70s, and the decade categories "1960s" and "1970s" contain non-rock music from those times. Latin contains music from Cuba, Argentina, Brazil, and Spain. Classical here refers to composed music in general, from 1300 to the present. Other includes music from Egypt, Iraq, Bulgaria, Jamaica, some African countries, and soundtracks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-84030768438615881?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/84030768438615881/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=84030768438615881' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/84030768438615881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/84030768438615881'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/04/visualising-musical-appetite.html' title='Visualising musical appetite'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-CWBD3BlOWlE/TbZCQk-LmyI/AAAAAAAAAbc/IKihyEaczEI/s72-c/s1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7204446212893545135</id><published>2011-02-11T23:57:00.000-08:00</published><updated>2011-02-12T00:09:01.445-08:00</updated><title type='text'>How to quickly multiply roman numerals</title><content type='html'>I used to think that it would be very slow to multiply roman numerals. A few days ago, I thought of an algorithm that is actually fairly quick. 
&lt;br/&gt;&lt;br/&gt;
The roman symbols themselves were slowing me down, so, my first step was to come up with a different set of numerals. One might call these "potato bug" numbers. (When converting from roman numerals, also expand any shorthand so that IV is expanded to IIII and so on). Each hatch mark means a factor of 10.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-mt6yweQdfYc/TVY9relmENI/AAAAAAAAAac/kqr5PMYFxb0/s1600/p1b.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 232px; height: 223px;" src="http://4.bp.blogspot.com/-mt6yweQdfYc/TVY9relmENI/AAAAAAAAAac/kqr5PMYFxb0/s320/p1b.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5572709406071984338" /&gt;&lt;/a&gt;
&lt;br/&gt;
and so on. This system is easier to remember and also happens to be quick to write:
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-slDTT5a08ac/TVY9z50qIhI/AAAAAAAAAak/TLla0dYXUXs/s1600/fig_2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 219px; height: 183px;" src="http://2.bp.blogspot.com/-slDTT5a08ac/TVY9z50qIhI/AAAAAAAAAak/TLla0dYXUXs/s320/fig_2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5572709550821876242" /&gt;&lt;/a&gt;
&lt;br/&gt;
Multiplication is now simple. Anything multiplied by I is the number itself. Anything multiplied by ✝  is the number times 10, which is to say: draw another hatch line on everything. When multiplying by 5, one can use the shortcuts in this table:
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-3ykI-1suWgs/TVY93pg8VdI/AAAAAAAAAas/iw31UeE5cuE/s1600/fig_3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 254px; height: 182px;" src="http://3.bp.blogspot.com/-3ykI-1suWgs/TVY93pg8VdI/AAAAAAAAAas/iw31UeE5cuE/s320/fig_3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5572709615163692498" /&gt;&lt;/a&gt;
&lt;br/&gt;

To generalize the table - if either or both of the inputs have hatch marks, simply add that many hatch marks on the result. Easy to remember that multiplying by ✝ is just drawing another hatch mark on everything.
&lt;br/&gt;&lt;br/&gt;

One can multiply two numbers by using a "FOIL" like method by the distributive law. To multiply two numbers, take the left-most symbol of the left number, and distribute it across the symbols in the right number. The intermediary results can just be appended in one long string (because there is no place system, a ✝  is 10 wherever it is written). Then take the next symbol in the left number, and again distribute it across the symbols in the right number, and repeat. Finally, simplify the answer by grouping symbols.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-7Yiprw_sOL4/TVY96QTME5I/AAAAAAAAAa0/ihFV1UFzSvw/s1600/fig_4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 246px;" src="http://2.bp.blogspot.com/-7Yiprw_sOL4/TVY96QTME5I/AAAAAAAAAa0/ihFV1UFzSvw/s320/fig_4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5572709659934725010" /&gt;&lt;/a&gt;
&lt;br/&gt;
There are many operations, but each is trivial and done in a split second. When later "gathering", I would underline to help me remember what I had moved into a group of 5.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-EKn4ORXqvCY/TVY99KiVZLI/AAAAAAAAAa8/i9OT53ois-Q/s1600/fig_5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 242px;" src="http://2.bp.blogspot.com/-EKn4ORXqvCY/TVY99KiVZLI/AAAAAAAAAa8/i9OT53ois-Q/s320/fig_5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5572709709927244978" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-hjU6dOdCyLQ/TVY9__lgW1I/AAAAAAAAAbE/Nyov0cQcm1U/s1600/fig_6.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 203px;" src="http://4.bp.blogspot.com/-hjU6dOdCyLQ/TVY9__lgW1I/AAAAAAAAAbE/Nyov0cQcm1U/s320/fig_6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5572709758527363922" /&gt;&lt;/a&gt;
&lt;br/&gt;
So there you have it. Multiplying two digit numbers in seconds with some circles and scratches. Not as slow as you might think.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7204446212893545135?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7204446212893545135/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7204446212893545135' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7204446212893545135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7204446212893545135'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2011/02/how-to-quickly-multiply-roman-numerals.html' title='How to quickly multiply roman numerals'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-mt6yweQdfYc/TVY9relmENI/AAAAAAAAAac/kqr5PMYFxb0/s72-c/p1b.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5237765342306461142</id><published>2010-12-29T19:13:00.000-08:00</published><updated>2010-12-29T19:14:54.168-08:00</updated><title type='text'>Notespad 1</title><content type='html'>I've turned my dynamic compilation code towards audio. I wrote a program recently that is a "lab" for creating audio effects: you type in a few lines of code and press Go, and the code is compiled and run. These processing snippets can be saved, along with parameter settings. I jokingly call it Windows Notespad.
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_sIga-_J94mI/TRv4-kt2DpI/AAAAAAAAAZk/r7MmHTAp908/s1600/fig0b.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 146px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/TRv4-kt2DpI/AAAAAAAAAZk/r7MmHTAp908/s320/fig0b.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5556308319183179410" /&gt;&lt;/a&gt;
&lt;br/&gt;
With this tool I can quickly prototype audio effects. As a source I will use Cathy Berberian's reading from Joyce's Ulysses. (This is an excerpt from Luciano Berio's piece Thema, Omaggio a Joyce).
&lt;br/&gt;&lt;br/&gt;
Some interesting changes can be made in the frequency domain. I divide the audio into many overlapping pieces. If I take the dft of each piece, change the phases every 256 samples, and ifft back, this imparts a high tone to the sound (frequency = 44100hz / 256 = 689hz), turning the speaking into singing. If I make each piece 0.1 seconds, ramp the pieces for a smooth transition, and set the phases to random values, I can 'smear' the sound, keeping the frequency content within each piece but losing the envelopes. This resembles adding a lot of reverb.
&lt;br/&gt;&lt;br/&gt;
Hear:&lt;br/&gt;
&lt;object height="81" width="100%"&gt; &lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8550396"&gt;&lt;/param&gt; &lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8550396" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;
&lt;br/&gt;&lt;br/&gt;
The original, for comparison:&lt;br/&gt;
&lt;object height="81" width="100%"&gt; &lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8310102"&gt;&lt;/param&gt; &lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8310102" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;
&lt;br/&gt;
I wrote a script to plot amplitude (average signal power) over time. Plotted on a log scale, it looks like this:
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/TRv5Cm1jfQI/AAAAAAAAAZs/zYeIlLLkXao/s1600/thinput4.25b.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 174px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/TRv5Cm1jfQI/AAAAAAAAAZs/zYeIlLLkXao/s320/thinput4.25b.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5556308388471864578" /&gt;&lt;/a&gt;
&lt;br/&gt;
One thought was to turn this curve into pitch. What if one played a sine wave corresponding to these pitches? I experimented with code until I found an effect like a bird's song.
&lt;br/&gt;
&lt;object height="81" width="100%"&gt; &lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8550788"&gt;&lt;/param&gt; &lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8550788" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;
&lt;br/&gt;
Another non-linear transformation is to change the pitch based on power. Imagine a poor vocalist who goes sharp on loud notes and sings flat on soft notes. What if the whole band played this way? The effect is too comical to include.
&lt;br/&gt;&lt;br/&gt;
We can find the loudness at a certain point in the audio clip. The idea struck me: what does it sound like if all of the quiet parts are made loud, and the loud parts made quiet? If find the effect to be rather terrifying:
&lt;br/&gt;
&lt;object height="81" width="100%"&gt; &lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8310101"&gt;&lt;/param&gt; &lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8310101" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;
&lt;br/&gt;
There is much left to explore.
&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5237765342306461142?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5237765342306461142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5237765342306461142' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5237765342306461142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5237765342306461142'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/12/notespad-1.html' title='Notespad 1'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_sIga-_J94mI/TRv4-kt2DpI/AAAAAAAAAZk/r7MmHTAp908/s72-c/fig0b.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-452405670101688272</id><published>2010-11-06T18:53:00.000-07:00</published><updated>2010-11-07T23:07:58.667-08:00</updated><title type='text'>drawdraw</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.b3nf.com/alpha/drawdraw/main.html"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 306px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/TNegCiJ1W0I/AAAAAAAAAZI/IkyRNOw7mI4/s320/sv4.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5537070232263547714" /&gt;&lt;/a&gt;
&lt;br/&gt;
drawdraw is a web app for recursive drawing. &lt;a href="http://www.b3nf.com/alpha/drawdraw/main.html"&gt;Try it!&lt;/a&gt;

&lt;br/&gt;&lt;br/&gt;
Instructions: Click the buttons to make things. Drag the green dots to move things. Experiment until it looks cool. Send links.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.b3nf.com/alpha/drawdraw/main.html"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 237px; height: 240px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/TNeg756kOTI/AAAAAAAAAZY/xfR_DQV21x8/s320/sv3.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5537071217894504754" /&gt;&lt;/a&gt;

&lt;br/&gt;&lt;br/&gt;

Now that you have spent time playing with the program:&lt;br/&gt;
The light-blue arrow in the background represents the initial scale and rotation.  At every step, the orange arrows are replaced with a scaled and rotated copy of the figure. If an orange arrow is longer than the light-blue arrow, the figure "explodes" because the size increases with every step.
&lt;br/&gt;&lt;br/&gt;
In the end, if enough steps are done (the pencil+ button adds more steps), look at the result - the location where each orange arrow will contain the entire figure (picture a tree, where each branch can have the form of the entire tree). It is 
self-similar, a property held by fractals. Because this program represents a set of affine mappings, it can be used to draw all of the traditional IFS fractals in a new manner.
&lt;br/&gt;&lt;br/&gt;
Have fun!
&lt;br/&gt;&lt;br/&gt;
(I wonder how one would make a non-linear version...)
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.b3nf.com/alpha/drawdraw/main.html"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 298px; height: 291px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/TNeg2rNBlmI/AAAAAAAAAZQ/KsxmuS_zvKU/s320/sv2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5537071128046048866" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-452405670101688272?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/452405670101688272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=452405670101688272' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/452405670101688272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/452405670101688272'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/11/drawdraw.html' title='drawdraw'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_sIga-_J94mI/TNegCiJ1W0I/AAAAAAAAAZI/IkyRNOw7mI4/s72-c/sv4.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6995583900392520158</id><published>2010-10-23T13:51:00.000-07:00</published><updated>2010-10-23T13:56:26.892-07:00</updated><title type='text'>plotmydata</title><content type='html'>Need to plot some data quickly? Try &lt;a href="http://plotmydata.blogspot.com"&gt;plotmydata.blogspot.com&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://plotmydata.blogspot.com"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 270px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/TMNLzHFQn7I/AAAAAAAAAZA/e4eKtFKLQis/s320/plotmydata.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5531348108787621810" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Your data is plotted in real time as you type, or you can copy/paste in your data in csv format.
&lt;br/&gt;&lt;br/&gt;
This is for the times when you don't want to wait for Matlab to open, and it's not worth it to write a python matplotlib script/use a command line tool.
&lt;br/&gt;&lt;br/&gt;
What features do you want - uploading text files? scatter plots? zooming? comparing multiple trends? plotting an arbitrary function? anything else?
I'm all ears.
&lt;br/&gt;&lt;br/&gt;
Just one of those projects. It makes use of Stanford's &lt;a href="http://vis.stanford.edu/protovis/"&gt;protovis&lt;/a&gt; library.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6995583900392520158?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6995583900392520158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6995583900392520158' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6995583900392520158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6995583900392520158'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/10/plotmydata.html' title='plotmydata'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_sIga-_J94mI/TMNLzHFQn7I/AAAAAAAAAZA/e4eKtFKLQis/s72-c/plotmydata.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5742055510179986105</id><published>2010-09-03T22:44:00.000-07:00</published><updated>2010-09-03T23:07:08.528-07:00</updated><title type='text'>pixels</title><content type='html'>I've taken a break from writing music this summer and have done this instead. 
&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;


&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/TIHdzXR7hyI/AAAAAAAAAYQ/4V3am2V2BCw/s1600/02.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 314px; height: 311px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/TIHdzXR7hyI/AAAAAAAAAYQ/4V3am2V2BCw/s400/02.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5512931293370484514" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/TIHeQV52ZgI/AAAAAAAAAYg/1HJ92nc7zNc/s1600/03.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 373px; height: 380px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/TIHeQV52ZgI/AAAAAAAAAYg/1HJ92nc7zNc/s400/03.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5512931791217255938" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;


&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/TIHeQJRPGUI/AAAAAAAAAYY/7BvqT-d-XTc/s1600/01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 384px; height: 384px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/TIHeQJRPGUI/AAAAAAAAAYY/7BvqT-d-XTc/s400/01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5512931787825682754" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_sIga-_J94mI/TIHeRLSG7nI/AAAAAAAAAYo/B7WCSXppwxI/s1600/04.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 331px; height: 394px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/TIHeRLSG7nI/AAAAAAAAAYo/B7WCSXppwxI/s400/04.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5512931805546081906" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;



&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/TIHeR05u2VI/AAAAAAAAAYw/eutSknkGr18/s1600/05.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 388px; height: 386px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/TIHeR05u2VI/AAAAAAAAAYw/eutSknkGr18/s400/05.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5512931816718129490" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;

&lt;!-- to simultaneously evoke the natural and digital worlds --&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5742055510179986105?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5742055510179986105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5742055510179986105' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5742055510179986105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5742055510179986105'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/09/pixels.html' title='pixels'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/TIHdzXR7hyI/AAAAAAAAAYQ/4V3am2V2BCw/s72-c/02.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-149950584619373606</id><published>2010-08-18T18:10:00.000-07:00</published><updated>2010-08-18T18:25:14.193-07:00</updated><title type='text'>Thread Unsafety</title><content type='html'>&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;#!/usr/bin/env python
import time,itertools
from threading import Thread

class FizzBuzz():
  g_place = ''
  def th(self, t, tminc, fn):
    start = time.clock()
    while True:
      t +=tminc
      while time.clock()&amp;lt;start+t: time.sleep(0.01)
      self.g_place = fn(self.g_place)
    
  def go(self, counter, printer):
    thds = ((0.7, 1.0, lambda c: printer(c)), 
      (0.0, 1.0, lambda c: counter.next()),
      (0.1, 3.0, lambda c: 'Fizz'), 
      (0.2, 5.0, lambda c: 'FizzBuzz' if c=='Fizz' else 'Buzz'))
    for thd in thds: Thread(target=self.th, args=thd).start()

def _print(s): print s
FizzBuzz().go(itertools.count(1), _print)
&lt;/code&gt;&lt;/pre&gt;
As far as I know, this is a unique way to code FizzBuzz. It's also a pretty bad way, for a variety of reasons. But at least it's multithreaded!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-149950584619373606?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/149950584619373606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=149950584619373606' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/149950584619373606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/149950584619373606'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/08/thread-unsafety.html' title='Thread Unsafety'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6112564323650035183</id><published>2010-08-14T00:10:00.000-07:00</published><updated>2010-08-14T00:17:05.353-07:00</updated><title type='text'>Physics into audio</title><content type='html'>I've been toying with the idea of using physics simulations to generate audio.
&lt;br/&gt;&lt;br/&gt;
First, imagine a audio in a circular loop, as on a record player. As the needle moves clockwise, the music plays. If the needle moves counter-clockwise, the music is played backwards. The faster the needle moves, the faster (and higher pitched) the sound.
&lt;br/&gt;&lt;br/&gt;
Now, model the chaotic motion of a double pendulum, and use that as the needle that is driving the music:
&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=14134620&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=1&amp;amp;color=&amp;amp;fullscreen=1&amp;amp;autoplay=0&amp;amp;loop=0" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=14134620&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=1&amp;amp;color=&amp;amp;fullscreen=1&amp;amp;autoplay=0&amp;amp;loop=0" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;br/&gt;&lt;br/&gt;
It can also be interesting to use the data directly. Take a physics model of a double pendulum, and record the height (y) of the longer leg over time. Then, interpret that data as raw audio samples and play it!
&lt;br/&gt;&lt;br/&gt;
&lt;object height="81" width="100%"&gt; &lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fsoundcloud.com%2Fjamonben%2Fdouble-pendulum-as-raw-audio&amp;secret_url=false"&gt;&lt;/param&gt; &lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fsoundcloud.com%2Fjamonben%2Fdouble-pendulum-as-raw-audio&amp;secret_url=false" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt; &lt;/object&gt;  &lt;span&gt;&lt;a href="http://soundcloud.com/jamonben/double-pendulum-as-raw-audio"&gt;Double pendulum as raw audio&lt;/a&gt; by &lt;a href="http://soundcloud.com/jamonben"&gt;jamonben&lt;/a&gt;&lt;/span&gt; 
&lt;br/&gt;&lt;br/&gt;
I inadvertently created a &lt;a href="http://soundcloud.com/jamonben/afterlife-bongo-talkin"&gt;mashup&lt;/a&gt; while working on a related project, but it's not very good.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6112564323650035183?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6112564323650035183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6112564323650035183' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6112564323650035183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6112564323650035183'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/08/physics-into-audio.html' title='Physics into audio'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-777189314954886035</id><published>2010-08-02T22:04:00.000-07:00</published><updated>2010-08-02T22:50:25.885-07:00</updated><title type='text'>LnzScript 0.4</title><content type='html'>The LnzScript 0.4 release is &lt;a href="http://code.google.com/p/launchorz"&gt;here&lt;/a&gt;! This scripting tool can automate repetitve tasks in Windows, work with files and folders, and easily perform text manipulation.
&lt;br/&gt;&lt;br/&gt;
The editor makes it easy to write quick one-off scripts. Standard output appears in a pane to the right. 
Let's say you have some text that you want shuffled randomly. If one were to write a Python script, you'd have to save the text
in a temporary file, or load some library to access the Windows clipboard. With LnzScript, the clipboard is directly accessible without importing anything. One could just write Clipboard.set(Math.shuffleArray(Clipboard.get().split('\r\n'))) Furthermore, the LnzScript Editor can run a script before it has even been saved to a file, a unique feature, so you can run a script immediately. 
&lt;br/&gt;&lt;br/&gt;
LnzScript 0.4 includes more methods for working with files. File.dirListFiles(@'c:\myfolder', '*.txt','size') returns all of the text files in the directory, sorted by size. File.copyMany can copy full directories. File.createShortcut and File.getShortcutTarget can work with shortcuts. The Rename.renameFn will call a function you provide to rename files. To rename .jpeg to .jpg, say, all you need to do is: &lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;File.cd(@'c:\myfolder');
Rename.rename('.jpeg','.jpg');
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;
Rename.renamePreview can be used to see the results before renaming anything. 
&lt;br/&gt;&lt;br/&gt;
And, it's still good at automating tasks. I used it to access Webmail (for which there was not a 'remember me' option); much faster than typing in the fields every time. An old demo of LnzScript opening Firefox tabs is &lt;a href="http://www.b3nf.com/codepages/lnz/movlnz_02_screencast_fftabs.htm"&gt;here&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;

The installer associates script files so that you can run them by double-clicking. Just by using argv in a script, you can now drag and drop a file
into the script and it will run with that argument in argv[1], a 'droplet.' Lately I've been using Clavier+ to assign scripts to hotkeys. Other programs such as Launchy could also be used.
&lt;br/&gt;&lt;br/&gt;

Why JavaScript? I find it to be much cleaner than Basic-derived semi-languages. Lambdas and 
&lt;a href="http://www.youtube.com/watch?v=hQVTIJBZook" &gt;the good parts&lt;/a&gt; can be very useful. I've added @'string' literals to the language, that can
contain single backslashes, so that one can type @'c:\foo' instead of 'c:\\foo'. There are other additions like include(), alert(), print(), and argv.
&lt;br/&gt;&lt;br/&gt;


Parsing .url shortcuts in a directory and printing out URLs:&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;File.cd(@'C:\folder');
var arFiles = File.dirListFiles('.', '*.url')
for(var i=0; i&amp;lt;arFiles.length;i++)
{
  var pageName = arFiles[i].slice(0,-4)
  var url = File.readFile(arFiles[i]).split('URL=')[1].split('\n')[0]
  print(pageName)
  print(url + '\n')  
}

&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;&lt;br/&gt;

An example using JavaScript's closures to rename files in the pattern 00, 01, 02, and so on, by last modification time:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;include('&amp;lt;std&amp;gt;')
File.cd(@'C:\folder')
var i=0;
function renameNumbered(s)
{
  i++;
  return (i&amp;lt;10)?'0'+i+s : i+s;
}
//first, preview to see consequences
Rename.renameFnPreview(renameNumbered, 'time')
//then,
//Rename.renameFn(renameNumbered, 'time')
&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-777189314954886035?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/777189314954886035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=777189314954886035' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/777189314954886035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/777189314954886035'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/08/lnzscript-04.html' title='LnzScript 0.4'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2754971142343602697</id><published>2010-07-14T21:56:00.000-07:00</published><updated>2010-07-14T22:02:17.560-07:00</updated><title type='text'>Audiovisual</title><content type='html'>I few months ago I was struck with an idea. What would it look like if traditional audio effects were applied to videos? 
&lt;br/&gt;&lt;br/&gt; 
I could treat every pixel as a separate signal in time, with its brightness being the value. 
These become sequences of values, just like streams of audio data.
I could put these signals through various audio filters, like high-pass, low-pass, and so on.
(I solely apply effects in time, not space.) 
&lt;br/&gt;&lt;br/&gt;
I wrote code in C that worked on individual frame images at the pixel level in order to have complete control.
&lt;br/&gt;&lt;br/&gt;
Applying a low pass filter to all pixels created a motion-blur like effect, because it was essentially blurring together consecutive frames. 
A high pass filter looked more interesting - the video became all dark except for the areas with the fastest motion. 

Things really started to get interesting, though, when I used other audio effects. Watch this:
&lt;br/&gt;&lt;br/&gt;
&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=13182973&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=13182973&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;br/&gt;&lt;br/&gt;

A &lt;a href="http://en.wikipedia.org/wiki/Flanger"&gt;flange effect&lt;/a&gt; can be created with is the sum of two signals: one is the input signal, unchanged, and the other
is the input signal alternating between being played slightly faster, and slightly slower. Because of the alternation, they stay in sync, but
the effect is heard as a moving, shimmering sound. (Used in guitars in psychedelic rock).
&lt;br/&gt;&lt;br/&gt;

I applied an exaggerated version of this effect. I combine the video signal with an altered version of itself that speeds up and slows down,
just like a flange effect. This means that sometimes the altered signal is a ghostly signal sometimes ahead in the future, and sometimes lagging behind. 
&lt;br/&gt;&lt;br/&gt;
The effect with rainbow colors is related to a high pass filter - the areas that are changing the most are the ones that are allowed to overflow into vivid colors.
&lt;br/&gt;&lt;br/&gt;
In audio, there are methods of looping a non-periodic sound. One way is to play many copies of the sound offset in time, but giving each a envelope, so that
each piece on its own fades in and out, and isn't very noticeable. I used this in the last effect of the video, creating a loop.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2754971142343602697?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2754971142343602697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2754971142343602697' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2754971142343602697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2754971142343602697'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/07/audiovisual.html' title='Audiovisual'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7999430883274989273</id><published>2010-07-06T19:13:00.000-07:00</published><updated>2010-07-13T17:31:37.308-07:00</updated><title type='text'>Launchorz scripts</title><content type='html'>My &lt;a href="http://b3nf.com/codepages/lnz/index.html"&gt;Launchorz&lt;/a&gt; project makes it easy to automate repetitive tasks in Windows. 
Here are some of the scripts I've found to be the most useful.
(Note that they are intended for Windows 7 and might not be compatible with something earlier.)
&lt;br/&gt;&lt;br/&gt;


&lt;h3&gt;Open command-line to the current Explorer directory&lt;/h3&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;include('getExplorerDirectory.js');
Window.activate( {'class':'CabinetWClass'});
// activate the most-recently-used Explorer window.
Time.sleep(100);
var strDir = getCurrentExplorerDirectory();
if (strDir)
  Process.openFile('cmd.exe', strDir);

&lt;/code&gt;&lt;/pre&gt;

You can download &lt;a href="http://www.neednom.com/hhhblog/getExplorerDirectory.js"&gt;getExplorerDirectory.js&lt;/a&gt;. 

&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;Creating an index of all files in a directory&lt;/h3&gt;
This script creates a text file index of all files in a directory, including subdirectories.
The output is formatted nicely by indentation level.
It uses the path of the currently open Explorer window and creates tree_files.txt.
&lt;br/&gt;

&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;include('getExplorerDirectory.js');

Window.activate( {'class':'CabinetWClass'})
Time.sleep(100);
var strDir = getCurrentExplorerDirectory();
if (strDir)
  Process.open('cmd.exe /c tree /f /a &amp;gt; tree_files.txt', strDir);

&lt;/code&gt;&lt;/pre&gt;
 
&lt;h3&gt;Expand repeated code&lt;/h3&gt;
When working on a quick project, or writing in a language like Verilog, sometimes the same line of code
is repeated a few times, with a different index. The following script takes a code fragment like &lt;br/&gt;
a[#] = b[#] &amp;amp; c[#];&lt;br/&gt;
and expands it to&lt;br/&gt;
a[0] = b[0] &amp;amp; c[0];&lt;br/&gt;
a[1] = b[1] &amp;amp; c[1];&lt;br/&gt;
a[2] = b[2] &amp;amp; c[2];&lt;br/&gt;
...&lt;br/&gt;
(This isn't a useful example but it depicts what the script does).
All it does is replace the '#' character with a number.
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;include('&amp;lt;std&amp;gt;');
var sClipboard = Clipboard.getText();
if (sClipboard  &amp;amp;&amp;amp; sClipboard.contains('#'))
{  
  var sRep = Dialog.input('Replace', 'How many repetitions? n=0 to ?', '3');
  if (isNumber(sRep))
  {    
    var sResult = '';
    for (var i=0; i&amp;lt;sRep; i++)
      sResult += sClipboard.replace(/#/g, i) + '\r\n';
    Clipboard.setText(sResult);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Sort lines in the clipboard&lt;/h3&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;var strClipboard = Clipboard.getText();
if (strClipboard) 
{
  var arr = strClipboard.split('\r\n');
  arr.sort();
  var strResult = arr.join('\r\n');
  Clipboard.setText(strResult);
}
&lt;/code&gt;&lt;/pre&gt;

(You could also normalize the line endings with strClipboard.replace(/\r\n/g, '\n') and split on \n).


&lt;h3&gt;Displaying an ANSI table&lt;/h3&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;//opens an ascii table in notepad.
var s ='';
for (var i=32; i&amp;lt;128; i++)
{
  var si = (i&amp;lt;100)?' '+i : i.toString();
  s+= si + '\t' + String.fromCharCode(i);
  s+= '\r\n';
}
File.writeFile( File.getPathTemp()+'\\tmpascii.txt', s);
Process.open('notepad.exe '+File.getPathTemp()+'\\tmpascii.txt');
&lt;/code&gt;&lt;/pre&gt;

Showing a simple reference of ansi characters.



&lt;br/&gt;&lt;br/&gt;
I also have a clipboard replacement script that gives you 26 different clipboards, each recalled by a letter. 
However, there exist many other clipboard-handling tools.
&lt;br/&gt;&lt;br/&gt;
To learn more about Launchorz, you can watch some screencasts &lt;a href="http://b3nf.com/codepages/lnz/lnz_02_examples.html"&gt;here&lt;/a&gt;. 
(It is similar to AutoIt, but has a full JavaScript language and namespaces).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7999430883274989273?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7999430883274989273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7999430883274989273' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7999430883274989273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7999430883274989273'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/07/launchorz-scripts.html' title='Launchorz scripts'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7948040413472002236</id><published>2010-06-02T23:41:00.000-07:00</published><updated>2010-06-03T13:06:49.514-07:00</updated><title type='text'>Chaotic Maps!</title><content type='html'>This is the first release of "fastmapmap", a program I wrote that plots 2D chaotic maps. 
A discrete chaotic map will take the point (x,y) to a new (x_, y_) based on a function. 
For example, the Henon map takes the point (x,y) to (y+1-a*x*x, b*x), where a and b are constants.
One can draw a "portrait" of a map by choosing an (x0, y0) and repeatedly evaluating the function,
shading in each point that is landed on.
Depending on the values of a and b, sometimes the point moves off to become very large, or sometimes
the point alternates jumping between two or more values. Other times, the points shade in a region in a complicated way, 
never repeating, which is a chaotic orbit.
&lt;br/&gt;&lt;br/&gt;

The program fastmapmap can draw these portraits in real time. (I put some effort into optimization). The parameters a and b can be quickly changed
with the arrow keys. The plot can be easily navigated; hold alt and drag the mouse to zoom in on a region.&lt;br/&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/TAdjeJ6fy_I/AAAAAAAAAX0/1pkcMK4riDI/s320/srcn.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478456841427995634" /&gt;
&lt;br/&gt;

The program is called fastmapmap because an additional plot can be drawn that is a 
"map" of the behavior of the map. The x axis represents the constant a and the y axis is b.
In this plot, black areas are periodic, red areas escape to infinity, and colored areas are chaotic.
So, the plot can depict which parameter values may be interesting to observe.
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 244px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/TAdjesyDLaI/AAAAAAAAAX8/zAhuqWU7DH4/s320/screenshot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478456850787806626" /&gt;
&lt;br/&gt;

It's cross-platform, tested in Windows and Ubuntu. Try it! 
Download &lt;a href="http://www.neednom.com/hhhblog/sdl/fastmapmap0.1.zip"&gt;Windows&lt;/a&gt;  | &lt;a href="http://www.neednom.com/hhhblog/sdl/fastmapmapsrc0.1.tar.gz"&gt;Source&lt;/a&gt; (GPL)
&lt;br/&gt;&lt;br/&gt;
A short tutorial:
&lt;ul&gt;
&lt;li&gt;Open fastmap.exe&lt;/li&gt;
&lt;li&gt;Tap the right arrow a bit to change the value of a.&lt;/li&gt;
&lt;li&gt;Press Ctrl-s, type 'test', and press Enter to save as test.cfg&lt;/li&gt;
&lt;li&gt;Ctrl-click on the shape to zoom in, Shift-click to zoom out&lt;/li&gt;
&lt;li&gt;Press Ctrl-o, type 'test' and press Enter to open our test.cfg&lt;/li&gt;
&lt;li&gt;Press Alt-b and watch the shapes move&lt;/li&gt;
&lt;li&gt;Press Alt-b again to stop movement&lt;/li&gt;
&lt;li&gt;Click the cross-hairs icon to show the diagram. Click in the diagram.&lt;/li&gt;
&lt;li&gt;Click the "..." icon to read what else can be done.&lt;/li&gt;
&lt;/ul&gt;

The program can easily create animations. Hit Alt-B and watch the shapes start to move. Use Ctrl-F1 through Ctrl-F9 to set a keyframe. Refer to 
readme.txt for all of the other features.
&lt;br/&gt;&lt;br/&gt;

&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12244190&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12244190&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12252840&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12252840&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12251727&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12251727&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

I came up with other drawing modes. In the following, x and y represent x0 and y0, and the color is chosen according to the value after 20 iterations.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/TAdadFVjtiI/AAAAAAAAAXk/rzEnGxtFR-U/s1600/skm2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 306px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/TAdadFVjtiI/AAAAAAAAAXk/rzEnGxtFR-U/s320/skm2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478446927414801954" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/TAdacGMEL-I/AAAAAAAAAXU/FiPh4JbOXb8/s320/cl.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478446910463553506" /&gt;
&lt;br/&gt;
Assigning colors based on quadrant:&lt;br/&gt;
&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12255614&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12255614&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br/&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/TAdacgf2cOI/AAAAAAAAAXc/fDBKm7Sagg8/s320/quadrant1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478446917525860578" /&gt;&lt;br/&gt;&lt;br/&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/TAdadiRzMlI/AAAAAAAAAXs/dHbCj9XKNL0/s320/sunwl.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478446935183667794" /&gt;
&lt;br/&gt;&lt;br/&gt;

I invented this drawing method originally as way of depicting the action of the Henon map.
&lt;br/&gt;
&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12253319&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12253319&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 306px; height: 320px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/TAdZM6xWB3I/AAAAAAAAAXM/2jFIF4RjseA/s320/shlls.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478445550189021042" /&gt;&lt;br/&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 286px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/TAdZAsuQ0rI/AAAAAAAAAXE/89Np4Bo4vMg/s320/hnlnk.bmp" border="0" alt=""id="BLOGGER_PHOTO_ID_5478445340259570354" /&gt;

&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=12274411&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=12274411&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

Remember to download: &lt;a href="http://www.neednom.com/hhhblog/sdl/fastmapmap0.1.zip"&gt;Windows&lt;/a&gt;  | &lt;a href="http://www.neednom.com/hhhblog/sdl/fastmapmapsrc0.1.tar.gz"&gt;Source&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7948040413472002236?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7948040413472002236/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7948040413472002236' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7948040413472002236'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7948040413472002236'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/06/chaotic-maps.html' title='Chaotic Maps!'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_sIga-_J94mI/TAdjeJ6fy_I/AAAAAAAAAX0/1pkcMK4riDI/s72-c/srcn.PNG' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-645141869466588105</id><published>2010-06-02T13:32:00.001-07:00</published><updated>2010-06-02T13:52:05.116-07:00</updated><title type='text'>SDL Text and Dialogs</title><content type='html'>For one of my recent projects, I wanted dialogs in an sdl program.
This small library draws simple "dialogs" to ask the user for
text or a number. 
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/TAa_3IkM6fI/AAAAAAAAAW8/_BcMwNt4MJY/s1600/demo.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 294px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/TAa_3IkM6fI/AAAAAAAAAW8/_BcMwNt4MJY/s320/demo.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5478276950655822322" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Usage:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;// where SDL_Surface* pSurface is the screen surface.
char * s; 
s= Dialog_GetText(&amp;quot;Enter your name:&amp;quot;, &amp;quot;Fred&amp;quot;, pSurface);
Dialog_Messagef(pSurface, &amp;quot;Welcome, %s&amp;quot;, s);
free(s);

double val = 1.5;
Dialog_GetDouble(&amp;quot;Enter a value&amp;quot;, pSurface, &amp;amp;val);
int n = 1;
Dialog_GetInt(&amp;quot;Choose a number.&amp;quot;, pSurface, &amp;amp;n);
if (Dialog_GetBool(&amp;quot;Double the number?&amp;quot;, pSurface))
  n*=2;
Dialog_Message(&amp;quot;Goodbye&amp;quot;, pSurface);
freeFonts();
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;&lt;br/&gt;
To draw text, I used the code at &lt;a href="http://cone3d.gamedev.net/cgi-bin/index.pl?page=tutorials/gfxsdl/tut4"&gt;this page&lt;/a&gt;.
(I added the ability to have newlines and tabs). It supports lower ascii chars, and code could be improved, but currently suits my purposes. There's also SFont and others.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://www.neednom.com/hhhblog/sdl/fontsrc.tar"&gt;Source&lt;/a&gt; (includes a bitmap font).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-645141869466588105?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/645141869466588105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=645141869466588105' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/645141869466588105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/645141869466588105'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/06/sdl-text-and-dialogs.html' title='SDL Text and Dialogs'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_sIga-_J94mI/TAa_3IkM6fI/AAAAAAAAAW8/_BcMwNt4MJY/s72-c/demo.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7398339194361216539</id><published>2010-04-25T14:22:00.000-07:00</published><updated>2010-06-16T01:41:10.483-07:00</updated><title type='text'>SSE instructions</title><content type='html'>For the past few weeks I have been experimenting with &lt;a href="http://en.wikipedia.org/wiki/Streaming_SIMD_Extensions"&gt;Streaming SIMD Extension&lt;/a&gt; instructions. 
It takes a while to write at this lower level, but I've been able to get my code to run 3 times faster!
&lt;br/&gt;&lt;br/&gt;
Most processors since about 1999 contain eight 128 bit registers that can be used to do multiple computations in one instruction. 128 bits can hold 4 32-bit floating point numbers, and so 
there could be a theoretical 4x improvement in speed, as long as the same operation is done to each. For example, if you have two arrays a and b , and want to sum them, instead of: &lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;float a[4]; float b[4]; float c[4];
//...
c[0] = a[0]+b[0];
c[1] = a[1]+b[1];
c[2] = a[3]+b[2];
c[3] = a[3]+b[3];
&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;
, this can be expressed as:&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;__m128 a, b, c;
// ...
c = _mm_add_ps (a, b);
&lt;/code&gt;&lt;/pre&gt;
The _mm_add_ps "instrinsic" becomes 1 instruction on the cpu (addps) instead of 4. Similar code can be written with 32-bit integers, and a few other data types. (Technology like CUDA is faster, though). In case you are interested, here is an example of optimizing with SSE instructions.
&lt;br/&gt;&lt;br/&gt;
The following code will draw a chaotic map (c1 and c2 are parameters). For this particular map, we know that initial points or "seeds" close to (0.0,0.0) are good starting points.
Let's say, for fun, that we want this to run as fast as possible.
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;const int nseeds=4;
float xseeds[nseeds] = {0.0f, 0.0f, 0.0f, 0.0f};
float yseeds[nseeds] = {0.000001f, 0.000002f,-0.0000011f,-0.0000011f};
float x, y, x_, y_;
for (int seed=0; seed&amp;lt;nseeds; seed++)
{
  x=xseeds[seed]; y=yseeds[seed];
  // ... settling code ...
  
  for (int i=0; i&amp;lt;PixelsToDrawPerSeed; i++)
  {
    x_ = c1*x - y*y; 
    y_ = c2*y + x*y;
    x=x_;
    y=y_;
    if (ISTOOBIG(x)&amp;#124;&amp;#124;ISTOOBIG(y)) break; 
    //if escaping to infinity, there won't be more points drawn, so quit early.
    //quitting early is important, far slower without this.

    //scale to pixel coordinates, and plot point if it is within bounds. X1 is greatest x, and so on.
    int px = (int)(SIZE * ((x - X0) / (X1 - X0)));
    int py = (int)(SIZE * ((y - Y0) / (Y1 - Y0)));
    if (py &amp;gt;= 0 &amp;amp;&amp;amp; py &amp;lt; SIZE &amp;amp;&amp;amp; px&amp;gt;=0 &amp;amp;&amp;amp; px&amp;lt;SIZE)
      arr[px+py*SIZE] = 1; //plot point at px, py.
  }
}
&lt;/code&gt;&lt;/pre&gt;

Runtime for 1,310,720 different parameters is Test 1: 14,747ms, Test 2: 25,026ms.
First, I added threading to to take advantage of the two cpu cores. I made sure to balance the work so that each thread is kept busy.
This improved time to Test 1:7,452ms Test 2:12,617ms.
&lt;br/&gt;&lt;br/&gt;
At first glance, it's not obvious how this could be optimized. I was a bit concerned about the (int) casts becoming a slow _ftol call, but 
looking at the generated assembly showed that this wasn't the case. (Read &lt;a href="http://www.stereopsis.com/sree/fpu2006.html"&gt;here&lt;/a&gt;,&lt;a href="http://www.stereopsis.com/FPU.html"&gt;here&lt;/a&gt; for why casts can be slow, although with modern compilers and instruction sets this doesn't seem to be an issue).
&lt;br/&gt;&lt;br/&gt;
In visual studio, I noticed a setting for Streaming SIMD Extensions 2 (/arch:SSE2). 
With this enabled, the compiler was able to vectorize some of the code (I verified in the asm that it was using the xmm registers),
which resulted in an ok speed increase.
Test 1:5,981ms Test 2:10,987ms
&lt;br/&gt;&lt;br/&gt;
Can I do better?
&lt;br/&gt;&lt;br/&gt;
Here is my first attempt at a vectorized version, moving all 4 points at once.
&lt;br/&gt;

&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;__m128 x128 = _mm_setr_ps( 0.0f, 0.0f, 0.0f, 0.0f);
__m128 y128 = _mm_setr_ps( 0.000001f, 0.000002f,-0.0000011f,-0.0000011f);
__m128 mConst_a = _mm_set1_ps(c1); //sets all 4 fields to c1
__m128 mConst_b = _mm_set1_ps(c2);
__m128 nextx128, tempx, tempy;

__m128i const_size = _mm_set1_epi32(SIZE), const_zero = _mm_set1_epi32(0);
// ... settling code ...
for (int i=0; i&amp;lt;PixelsToDrawPerSeed; i++)
{
  nextx128 = _mm_sub_ps(_mm_mul_ps(mConst_a, x128), _mm_mul_ps(y128,y128));
  y128 = _mm_mul_ps(y128, _mm_add_ps(x128, mConst_b)); 
  x128 = nextx128;

  if (ISTOOBIG(x128.m128_f32[0]) &amp;#124;&amp;#124;ISTOOBIG(x128.m128_f32[1]) &amp;#124;&amp;#124;
    ISTOOBIG(x128.m128_f32[2]) &amp;#124;&amp;#124;ISTOOBIG(x128.m128_f32[3]) &amp;#124;&amp;#124;
    ISTOOBIG(y128.m128_f32[0]) &amp;#124;&amp;#124;ISTOOBIG(y128.m128_f32[1]) &amp;#124;&amp;#124;
    ISTOOBIG(y128.m128_f32[2]) &amp;#124;&amp;#124;ISTOOBIG(y128.m128_f32[3] ))
  {
    break;
  } //assume that if one is too big, all four will be too big, nearly always valid for this map

  //scale to pixel coordinates. assume that pixels are square, that X1-X0==Y1-Y0.
  //the following computes px = (int)(SIZE * ((x - X0) / (X1 - X0)));
  __m128 mConst_scale = _mm_set1_ps(SIZE / (X1 - X0));
  tempx = _mm_sub_ps(x128, _mm_set1_ps(X0));
  tempx = _mm_mul_ps(tempx, mConst_scale); 
  tempy = _mm_sub_ps(y128, _mm_set1_ps(Y0));
  tempy = _mm_mul_ps(tempy, mConst_scale); 
  __m128i xPt = _mm_cvttps_epi32(tempx); //cast to int32
  __m128i yPt = _mm_cvttps_epi32(tempy); 
  __m128i xInBounds =  _mm_and_si128(_mm_cmpgt_epi32(xPt, const_zero), _mm_cmplt_epi32(xPt, const_size));
  __m128i yInBounds =  _mm_and_si128(_mm_cmpgt_epi32(yPt, const_zero), _mm_cmplt_epi32(yPt, const_size));
  __m128i inBounds = _mm_and_si128(xInBounds, yInBounds);
  
  if (inBounds.m128i_i32[3] != 0)
    arr[xPt.m128i_i32[3] +yPt.m128i_i32[3]*SIZE] = 1; //plot point
  if (inBounds.m128i_i32[2] != 0)
    arr[xPt.m128i_i32[2] +yPt.m128i_i32[2]*SIZE] = 1; //plot point
  if (inBounds.m128i_i32[1] != 0)
    arr[xPt.m128i_i32[1] +yPt.m128i_i32[1]*SIZE] = 1; //plot point
  if (inBounds.m128i_i32[0] != 0)
    arr[xPt.m128i_i32[0] +yPt.m128i_i32[0]*SIZE] = 1; //plot point
}
&lt;/code&gt;&lt;/pre&gt;

It is convinient that conditions can be expressed in SSE with the and instruction and comparison instruction. 
If a comparison is true, the field is filled with 0xffffffff, and if false, 0x00000000.
This version was faster. Test 1: 4,650ms Test 2: 7,293ms
&lt;br/&gt;&lt;br/&gt;
It was clear from the generated assembly that the ISTOOBIG(x128.m128_f32[0]) ||ISTOOBIG(x128.m128_f32[1]
was inefficient, as it had to move data out of the xmm registers before comparing. I then learned about 
_mm_movemask_ps, which does exactly what I want. It condenses the 128 bits into 4 bits of an integer in one step, 
much faster than moving out data four times, and so I could write 
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;//instead of ISTOOBIG(x128.m128_f32[0])&amp;#124;&amp;#124;ISTOOBIG(x128.m128_f32[1]) and so on
__m128 estimateMag = _mm_add_ps(x128,y128); //estimate magnitude as (x+y)(x+y)
estimateMag = _mm_mul_ps(estimateMag,estimateMag);
__m128 isTooBig = _mm_cmpgt_ps(estimateMag, constThreshold);
if (_mm_movemask_ps(isTooBig) != 0) //if any of the four fields are true
  return 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;
This increased speed dramatically! The score is now Test 1: 1,956ms, Test 2: 3,323ms!
I later saw on &lt;a href="https://concatenative.org/wiki/view/SSE"&gt;this site&lt;/a&gt; assembly for a quick absolute value hack.
I used the idea and rewrote as 
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;__m128 estimateMag = _mm_add_ps(x128,y128); //estimate magnitude as abs(x+y)
estimateMag = _mm_andnot_ps(_mm_set1_ps(-0.0f), estimateMag); //credit: Concatenative language wiki
__m128 isTooBig = _mm_cmpgt_ps(estimateMag, constToobig);
if (_mm_movemask_ps(isTooBig) != 0) //if any of the four fields are true
  return 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;
A further optimization was to vectorize finding the array pixel location. Instead of "if (inBounds.m128i_i32[3] != 0)...", I could write:
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;//Make size a power of 2 so multiplication is a shift left. Example: SIZE=128,LOG2SIZE=7
__m128i pixel = _mm_add_epi32(xPt, _mm_slli_epi32(yPt, LOG2SIZE)); //pixel=xPt+yPt*size 
pixel = _mm_and_si128(inBounds, pixel); //draw out-of-bounds pixels at 0,0
arr[pixel.m128i_i32[3]] = 1; //plot point
arr[pixel.m128i_i32[2]] = 1; //plot point
arr[pixel.m128i_i32[1]] = 1; //plot point
arr[pixel.m128i_i32[0]] = 1; //plot point
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;
Note that I've eliminated some conditionals. The trick is to mask out the point if inBounds is false with the line "pixel = _mm_and_si128(inBounds, pixel);". 
False is represented by 0x00000000, and so if inBounds is false, pixel is set to 0.  All out-of-bounds pixels are drawn onto (0,0), but this pixel can be cleared later.
Speed is improved to Test 1: 1,726ms, Test 2: 2,991ms.
&lt;br/&gt;&lt;br/&gt;
If it were likely that a pixel would be out-of-bounds, I could change the order and check inbounds much earlier, before casting to int, and do things like checking if x is in bounds before calculating y.
Because this case not occur often, it is not worth attention.
&lt;br/&gt;&lt;br/&gt;
I notice some redundancy in "int py = (int)(SIZE * ((y - Y0) / (Y1 - Y0)))" and "pixel=x+y*size." In both, y is multiplied by a constant.
I could eliminate a multiply with something like:&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;int py_times_size = (int)(SIZE*SIZE* ((y - Y0) / (Y1 - Y0)))
if py_times_size&amp;gt;0 and py_times_size&amp;lt;SIZE*SIZE and x&amp;gt;0...
pixel=x+py_times_size.
&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;
This didn't work in sse vectors because of overflow with _mm_cvttps_epi32.
&lt;br/&gt;&lt;br/&gt;
My final step is the least elegant: &lt;a href="http://en.wikipedia.org/wiki/Loop_unwinding"&gt;loop unrolling&lt;/a&gt;. Repeating the loop body 4 times gave the fastest results. (Faster because less overhead and maybe better &lt;a href="http://software.intel.com/en-us/articles/preparing-applications-for-intel-coret-microarchitecture/"&gt;scheduling&lt;/a&gt;, there is a trade off because of cache misses as size increases).
There are additional savings because I only need one "isTooBig test"
per every four iterations, and so I deleted all but one of the tests from the loop. It doesn't matter if the point is too big for a few iterations as long as we realize it within a few iterations.
This final step also made a significant difference. 
&lt;br/&gt;&lt;br/&gt;Our final times are Test 1: 1,346ms, Test 2: 2,285ms. 
This amount of optimization, by about a factor of 4, can be the difference in a real-time program. 
The code in this example, by the way, makes it possible to quickly draw this rather striking figure:
&lt;br/&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;width: 198px; height: 200px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/TBiNJzHynPI/AAAAAAAAAYE/3PchEdwm6mw/s200/blog.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5483287745804803314" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7398339194361216539?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7398339194361216539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7398339194361216539' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7398339194361216539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7398339194361216539'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/04/sse-instructions.html' title='SSE instructions'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/TBiNJzHynPI/AAAAAAAAAYE/3PchEdwm6mw/s72-c/blog.PNG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5831039215281821655</id><published>2010-03-08T15:18:00.000-08:00</published><updated>2010-08-31T21:16:28.455-07:00</updated><title type='text'>Bifurcation</title><content type='html'>I wrote a program to draw bifurcation diagrams of any 1-dimensional map.&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_sIga-_J94mI/S5WGIbT5X1I/AAAAAAAAAWc/zTGaSg5wbEI/s1600-h/shot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 210px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/S5WGIbT5X1I/AAAAAAAAAWc/zTGaSg5wbEI/s320/shot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5446406803702767442" /&gt;&lt;/a&gt;

&lt;br/&gt;&lt;br/&gt;
Recently, I studied 1-dimensional maps in my nonlinear dynamics course. 
I recommend reading about this topic: it is fascinating that an expression
as simple as p_next = r*p*(1-p) can lead to what is known as "chaos." r is a constant,
 p is a number between 0 and 1, and the expression is repeatedly evaluated.
The resulting sequence of p can either settle to a 
fixed number, periodically cycle between values, or continue aperiodically and seemingly in a random fashion.
A &lt;a href="http://en.wikipedia.org/wiki/Bifurcation_diagram"&gt;bifurcation diagram&lt;/a&gt; concisely depicts
the behavior as the parameter r changes.
Read more about the logistic map on &lt;a href="http://en.wikipedia.org/wiki/Logistic_map"&gt;Wikipedia&lt;/a&gt;
or &lt;a href="http://hypertextbook.com/chaos/11.shtml"&gt;elsewhere&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
I used Matlab for most numerical work pertaining to the course. In my spare time, though, I wrote this program to experiment with 
different maps and parameters. Also, because the program runs so much more quickly, I could create better images without waiting.
(This program builds upon my earlier project that dynamically compiles code, and so it runs efficiently.)
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09other/csbifurcation_bin.zip"&gt;Download&lt;/a&gt; (Unzip and run CsBifurcation.exe). &lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09other/csbifurcation_src.zip"&gt;Source&lt;/a&gt; (GPLv3 license)
&lt;br/&gt;&lt;br/&gt;
To use the program, type in an expression, and click Go. By default, the famous logistic map is shown.
Use the mouse to zoom in. By holding the Alt key and dragging, you can zoom in on a non-square rectangle of the plot.
Refer to the readme.txt file for more information. I encourage you to run this program and explore the intricate details of the logistic map, and to invent your own 1D maps.
The program can also create animations by slowly changing one of the parameters over time. I will post some
of the animations I've made at a later date.
&lt;br/&gt;&lt;br/&gt;
As I wrote new expressions and drew bifurcation diagrams, I began to search for interesting shapes and details. 
I began to compose images based on appearance rather than mathematical properties.
A selection of this work:&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/S5WGg1LE3vI/AAAAAAAAAW0/1KXOD2A8q_4/s1600-h/sm_threads.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 318px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/S5WGg1LE3vI/AAAAAAAAAW0/1KXOD2A8q_4/s320/sm_threads.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5446407222961954546" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/S5WGbAiTy0I/AAAAAAAAAWk/Vnio_mQA-AM/s1600-h/sm_aesthetic-rand%23add_strands_bio.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/S5WGbAiTy0I/AAAAAAAAAWk/Vnio_mQA-AM/s320/sm_aesthetic-rand%23add_strands_bio.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5446407122932976450" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/S5WGeX0a_MI/AAAAAAAAAWs/JT9qDpsUaZY/s1600-h/sm_semicircle_tent_fold.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 319px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/S5WGeX0a_MI/AAAAAAAAAWs/JT9qDpsUaZY/s320/sm_semicircle_tent_fold.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5446407180722568386" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5831039215281821655?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5831039215281821655/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5831039215281821655' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5831039215281821655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5831039215281821655'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/03/bifurcation.html' title='Bifurcation'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_sIga-_J94mI/S5WGIbT5X1I/AAAAAAAAAWc/zTGaSg5wbEI/s72-c/shot.PNG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6260421302963823242</id><published>2010-02-09T11:13:00.000-08:00</published><updated>2010-08-31T21:14:50.744-07:00</updated><title type='text'>An Excessively Fast Graphing Calculator</title><content type='html'>Lately I've been exploring the code-generation features of the .NET framework. It's cool to be able to compile and run new code at runtime. The CSharpCodeProvider and System.CodeDom.Compiler classes provide this ability.
&lt;br/&gt;&lt;br/&gt;
Evaluating code at runtime is especially useful when accepting user scripts, or evaluating a new mathematical expression. In Python, one can use eval() or exec to run code, but in C# the process is more complicated. So, I wrote a wrapping class with a convenient interface, that can be reused in other projects.
&lt;br/&gt;&lt;br/&gt;
For example, 
public double simpleMathEval(string strExp, out string strErr)
can easily evaluate an expression:
double ret = simpleMathEval("1+3+Math.sin(4.6)", out strErr);
The more advanced method mathEval accepts any amount of code and inner functions.
&lt;br/&gt;&lt;br/&gt;
As a proof of concept, I wrote a graphing calculator. It's really fast - compiling your expression. When you click Plot, a new program is generated that is essentially a loop containing your expression. (Compilation or runtime errors are caught and relayed back to you). On the graph, zoom in easily by clicking and dragging to draw a rectangle.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/S3G0PyiEtuI/AAAAAAAAAWM/3iBc8ZpP14g/s1600-h/ss1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 226px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/S3G0PyiEtuI/AAAAAAAAAWM/3iBc8ZpP14g/s320/ss1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5436324408568624866" /&gt;&lt;/a&gt;&lt;br/&gt;
(Note that I try to be intelligent when drawing asymptotes, making this plot of cosecant look cleaner than if it'd been plotted in Matlab).

I based the code on the efforts of Mike Gold's "CodeDom Calculator", but made a new interface and extended it to allow arrays. As the title of this blog suggests, though, I haven't polished or thoroughly tested it. 
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/S3G0S9VKSEI/AAAAAAAAAWU/0OElOk0zYU4/s1600-h/ss2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 226px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/S3G0S9VKSEI/AAAAAAAAAWU/0OElOk0zYU4/s320/ss2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5436324463006861378" /&gt;&lt;/a&gt;
&lt;br/&gt;
A more advanced example - you can put all sorts of code in here.
&lt;br/&gt;&lt;br/&gt;
To see how to use CodedomEvaluator, CodedomTest.cs contains some examples.
CodedomEvaluator Usage:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;double simpleMathEval(string strExp, out string strErr)
-evaluates a string, not a full statement. example: &amp;quot;3.0*5.1&amp;quot;returns 15.3.
double simpleMathEval(string strExp, string strVarname, double varValue, out string strErr)
-same as above, but provide a variable. 
example: strVarname=&amp;quot;x&amp;quot;, varValue=3, &amp;quot;x*x&amp;quot; returns 9.0.
double mathEval(string strExp, Dictionary&amp;lt;string, double&amp;gt; vars, out string strErr)
-evaluates a string that is a full statement. must assign to &amp;quot;ans&amp;quot;. 
the vars dictionary can be used to provide variables.
example: vars[&amp;quot;x&amp;quot;] = 3.0, &amp;quot;ans=x+4;&amp;quot; returns 7.0.
double[] mathEvalArray(string strExp, Dictionary&amp;lt;string, double&amp;gt; vars, int arrayLen, out string strErr)
-like above, but will return an array of values. The array arrAns[] will be created of length arrayLen.
example: arrayLen=40, &amp;quot;for(int i=0;i&amp;lt;40;i++) arrAns[i]=i*i;&amp;quot;
Because the inner loop is compiled, 
this is a fast way to evaluate an expression many times.
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09other/csgraphingcalc_bin.zip"&gt;Windows binary&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09other/csgraphingcalc_src.zip"&gt;C# src&lt;/a&gt;, GPL v3.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6260421302963823242?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6260421302963823242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6260421302963823242' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6260421302963823242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6260421302963823242'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2010/02/excessively-fast-graphing-calculator.html' title='An Excessively Fast Graphing Calculator'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_sIga-_J94mI/S3G0PyiEtuI/AAAAAAAAAWM/3iBc8ZpP14g/s72-c/ss1.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6562134633177120154</id><published>2009-11-26T21:57:00.000-08:00</published><updated>2010-08-31T21:18:01.012-07:00</updated><title type='text'>An idea for music transcription.</title><content type='html'>Many musicians spend time transcribing music, using computer software such as Sibelius or Finale to enter notes into sheet music.  This process, though, could use improvement, particularly when a midi keyboard is not available. (In some programs the computer keyboard mapping is not intuitive; in Finale 2006, one enters notes by typing their letter names. The "g" key produces a g note and so on - but on a qwerty layout, this doesn't work well, because of the non-consecutive placement of a,b, and c. Also, I have yet to see a good way to select note duration.)  Both Finale and Sibelius have recently released new transcription features, such as Sibelius' keyboard window introduced in May 2009. Here I will introduce one of my ideas for faster music transcription.&lt;br/&gt;&lt;br/&gt;

Instead of following an arbitrary metronome when recording, my program lets the performer tap the pulse themselves. This lets the performer play more naturally, and also to slow down for parts of the music that are more complex. I recently finished a proof-of-concept of this system that needs only a standard computer keyboard. The keys are played like a piano, and the Tab key is tapped for every quarter note. It works well; I've already used it to transcribe some of a Bach cantata. &lt;br/&gt;&lt;br/&gt;

Some screenshots of the prototype, called "Trilling":&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_sIga-_J94mI/Sw9qoPREK6I/AAAAAAAAAVg/_pymC7yByCs/s1600/prog1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 246px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/Sw9qoPREK6I/AAAAAAAAAVg/_pymC7yByCs/s320/prog1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5408658917020674978" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/Sw9qrkKEZYI/AAAAAAAAAVo/-NXhpgmUHdE/s1600/prog2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 182px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/Sw9qrkKEZYI/AAAAAAAAAVo/-NXhpgmUHdE/s320/prog2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5408658974168081794" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
A short fragment, saved to MusicXML and imported by Finale:&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_sIga-_J94mI/Sw9q8dqvMVI/AAAAAAAAAVw/txRvNMXrF2o/s1600/02fin.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 45px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/Sw9q8dqvMVI/AAAAAAAAAVw/txRvNMXrF2o/s320/02fin.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5408659264483832146" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
The same fragment, saved to LilyPond:&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/Sw9q-VqsSCI/AAAAAAAAAV4/L67QDYjj6m0/s1600/02l.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 79px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/Sw9q-VqsSCI/AAAAAAAAAV4/L67QDYjj6m0/s320/02l.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5408659296695896098" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Even if you don't have Finale, Sibelius, or LilyPond installed, you can see a rough preview of the music, drawn by my little "scoreview" program:&lt;br/&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/Sw9rRiuFv4I/AAAAAAAAAWA/wtn4-AzQVoI/s1600/02.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 142px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/Sw9rRiuFv4I/AAAAAAAAAWA/wtn4-AzQVoI/s320/02.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5408659626617323394" /&gt;&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;

I plan to take this project forward, adding more features, and creating a truly useful tool for music transcription. &lt;br/&gt;&lt;br/&gt;
In order to export to MusicXml and LilyPond, I use code from an open source project called Mingus. I'm grateful for that project, which turned out to be very similar to what I needed. I added the capability of tied notes and changed some of the ways it writes musicxml files.&lt;br/&gt;&lt;br/&gt;

&lt;a href="http://neednom.com/bfisher/blog/09audio/trilled0.1.zip"&gt;Download source&lt;/a&gt;, requires Python 2.5, released under GPLv3.
&lt;br/&gt;
At the moment, it only runs on Windows because I am using the winsound module to play .wav files in real-time. (I'm not using midi output anymore because, if one doesn't have hardware midi, there is a noticeable delay during playback.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6562134633177120154?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6562134633177120154/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6562134633177120154' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6562134633177120154'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6562134633177120154'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/11/idea-for-music-transcription.html' title='An idea for music transcription.'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_sIga-_J94mI/Sw9qoPREK6I/AAAAAAAAAVg/_pymC7yByCs/s72-c/prog1.PNG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-493028538600150968</id><published>2009-11-22T21:29:00.001-08:00</published><updated>2010-08-31T21:18:37.514-07:00</updated><title type='text'>Doppler Effect Simulation</title><content type='html'>Here's a hacked-together-in-one-night project just like the ones from the old days.&lt;br/&gt;&lt;br/&gt;


&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_sIga-_J94mI/Swq9IaZ4HNI/AAAAAAAAAVA/wDBC1d9fmmA/s1600/ex1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/Swq9IaZ4HNI/AAAAAAAAAVA/wDBC1d9fmmA/s320/ex1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5407342254836161746" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/doppler/ex1.wav"&gt;ex1.wav&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;





This is a Doppler effect simulation, in order to create the sound of particles whizzing past you, in stereo. You, the "observer", are represented by the L and R rectangles. The green numbers represent points along the path of that the particle travels. One specifies the path by moving the position of the numbers, using the mouse. If your points are spread apart, the particle travels faster, and if the points are close, the particle moves slower. &lt;br/&gt;&lt;br/&gt;


It's fun to play with. I recommend downloading and trying it. By clicking the checkbox, a second particle can be added.&lt;br/&gt;&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/Swq9K4-UHsI/AAAAAAAAAVI/RRtaQ90Jl8Q/s1600/ex2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/Swq9K4-UHsI/AAAAAAAAAVI/RRtaQ90Jl8Q/s320/ex2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5407342297401794242" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/doppler/ex2.wav"&gt;ex2.wav&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/Swq9MyRbYaI/AAAAAAAAAVQ/nldlaXU9kMI/s1600/ex3.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/Swq9MyRbYaI/AAAAAAAAAVQ/nldlaXU9kMI/s320/ex3.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5407342329962652066" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/doppler/ex3.wav"&gt;ex3.wav&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/Swq9Ota0vaI/AAAAAAAAAVY/N8ZA-3g4aoo/s1600/ex4.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/Swq9Ota0vaI/AAAAAAAAAVY/N8ZA-3g4aoo/s320/ex4.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5407342363019623842" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/doppler/ex4.wav"&gt;ex4.wav&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;

The physics are only approximate, because this is an audio project and not a physics project. 
Each pixel corresponds to 2 meters.
The distance between L and R "observers" is deliberately exaggerated to get a better stereo effect.
The volume is scaled by 1/r instead of 1/(r^2), in order to hear the particle more easily when it is far away. This is something to explore further.&lt;br/&gt;&lt;br/&gt;

I was able to write the code quickly. The expression (V/(V+vS)) gives a frequency shift, so if this value is 1.5, the perceieved pitch is 1.5 times higher. It turns out that this frequency shift is very well suited to code. First, I generate many seconds of source audio, say a sine wave at a fixed frequency. Then, I walk through this source audio at the "frequency shift" rate, interpolating between values when needed. So if the "frequency shift" came to 1.3, I would take the 0th,1.3th,2.6th,3.9th, samples from the source audio as the output audio, which results in output audio with a 1.3 times higher pitch. The whole project simplified to this:&lt;br/&gt;&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;fPositionInSourceAudio=0.0;
V = 340.0; //speed of sound
//for each timestep:
//move x and y
//find distance between particle and observer
distance = Math.Sqrt((x-xMe)*(x-xMe) + (y-yMe)*(y-yMe));
vS = (distance - prevdistance) / dt;
freqShift = (V / (V + vS));

intensity = scale * (1 / distance);
for (int i = 0; i &amp;lt; dt*sampleRate; i++)
{
outputAudio[index+i] = intensity * interpolatedValue(sourceAudio, fPositionInSourceAudio); 
fPositionInSourceAudio += freqShift;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;&lt;br/&gt;

&lt;h2&gt;doppler_fx&lt;/h2&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09audio/doppler/doppler_fx.zip"&gt;Download&lt;/a&gt; (Windows), unzip and run Doppler.exe. No setup needed.&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09audio/doppler/doppler_fx_src.zip"&gt;Source&lt;/a&gt;, released under GPLv3.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-493028538600150968?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/493028538600150968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=493028538600150968' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/493028538600150968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/493028538600150968'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/11/doppler-effect-simulation.html' title='Doppler Effect Simulation'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_sIga-_J94mI/Swq9IaZ4HNI/AAAAAAAAAVA/wDBC1d9fmmA/s72-c/ex1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-741220281986923885</id><published>2009-11-16T13:20:00.000-08:00</published><updated>2010-08-31T21:19:01.699-07:00</updated><title type='text'>Computer keyboard as piano keyboard</title><content type='html'>&lt;br/&gt;A continuation of a program I wrote a few years back, this little app lets you use your computer keyboard as a piano keyboard:
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/SwHCViT2cTI/AAAAAAAAAUw/-2LyHbZQFTo/s1600/shot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 235px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/SwHCViT2cTI/AAAAAAAAAUw/-2LyHbZQFTo/s320/shot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5404814703064412466" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;br/&gt;It can be useful for recording fragments of melodies or songs. The results are saved as a standard .mid file. Also, because it supports easy multi-tracking, it can be fun to make a little song by combining a bass part, harmony, and melody with different instruments for each voice.
&lt;br/&gt;
&lt;br/&gt;(Midisketch sends out real time midi events. So, if a midi loopback driver like &lt;a href="http://www.maplemidi.com/Maple_driver.html"&gt;Maple&lt;/a&gt; or &lt;a href="http://nerds.de/en/loopbe1.html"&gt;LoopBe1&lt;/a&gt; is used, it can act like a midi input device as well. In fact, it might be interesting to have Midisketch install keyboard hooks like Ctrl-alt-a, Ctrl-alt-b, and so on, that would play the corresponding notes even when the program is minimized. Then it could be used just like a midi keyboard, and recognized as such by other software).
&lt;br/&gt;
&lt;br/&gt;Anyways, here is a short video showing the keyboard: 
&lt;br/&gt;
&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=7666401&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=7666401&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/7666401"&gt;Midisketch demo&lt;/a&gt; from &lt;a href="http://vimeo.com/user1051344"&gt;bngjbng&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
&lt;br/&gt;
&lt;br/&gt;Three or more note polyphony is supported, constrained only by limitations of most keyboards. The "C# MIDI Toolkit" by Leslie Sanford is used to send midi data to the Windows api. 
&lt;br/&gt;
&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/midisketch.zip"&gt;Download&lt;/a&gt;, win32. 
&lt;br/&gt;(Unzip the file and run Midisketch.exe. See also readme.txt. Requires .NET 2).
&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/midisketch_src.zip"&gt;Source&lt;/a&gt;. 
&lt;br/&gt;Released under Gplv3.
&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-741220281986923885?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/741220281986923885/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=741220281986923885' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/741220281986923885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/741220281986923885'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/11/computer-keyboard-as-piano-keyboard.html' title='Computer keyboard as piano keyboard'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/SwHCViT2cTI/AAAAAAAAAUw/-2LyHbZQFTo/s72-c/shot.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1732021966570170897</id><published>2009-11-11T18:28:00.000-08:00</published><updated>2010-08-31T21:19:15.640-07:00</updated><title type='text'>Audio: noise</title><content type='html'>I have been continuing some digital audio experiments.&lt;br/&gt;&lt;br/&gt;

I find red noise to sound calmer and more natural than white noise. (Red noise can be produced by integrating white noise). The way I produce red noise is to start with a single value, add a small random (positive or negative) sample to it, and then continue. In white noise, the samples are unconnected leaps, but for red noise, the samples do not change as drastically.Let's say one takes a small chunk of red noise, 220 samples long. If one plays this chunk repeatedly, a 200hz tone is heard. The tone is colored by the frequencies present in the initial chunk of noise.&lt;br/&gt;&lt;br/&gt;Now let's create 15 different chunks of red noise, each of length 220. If we play one of the chunks ten times, then the next ten times, then the next ten times, and so on, eventually repeating,the result is a nice "warble" at 200hz. The upper frequencies present change abruptly about 20 times a second, causing a robotic sound. This is a fun sound that can be used for various purposes.
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/Svt1DoPE9eI/AAAAAAAAAUg/RWfaBFIO_7A/s1600-h/out_r.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 85px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/Svt1DoPE9eI/AAAAAAAAAUg/RWfaBFIO_7A/s320/out_r.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5403040883161757154" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/out_r.wav"&gt;out_r.wav&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;However, what if our goal is to create a more natural sound? As mentioned before, a problem is that the frequency content changes abruptly. We want to create more subtle changes, while still having the presence of buzzing red noise.&lt;br/&gt;&lt;br/&gt;The solution I came up with was inspired by how the red noise was created in the first place. I still begin with a chunk of red noise. After playing it ten times, instead of switching to an entirely different chunk, I instead slightly alter the chunk: by adding small random (positive or negative) values to each of the 220 samples. In this way, the frequency content does not change suddenly, and yet it changes in an unpredictable and subtle way. I find the resulting tone to still be harsh and noise-like, but surprisingly natural and musical. In the past it has only been in the frequency domain that I've been able to produce natural sounds.&lt;br/&gt;&lt;br/&gt;Two examples of this "softened noise":&lt;br/&gt;
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/Svt0_fUVc3I/AAAAAAAAAUY/uMsGOJkyGgU/s1600-h/classic.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 104px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/Svt0_fUVc3I/AAAAAAAAAUY/uMsGOJkyGgU/s320/classic.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5403040812048413554" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09audio/softened.wav"&gt;softened.wav&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/Svt1HibotRI/AAAAAAAAAUo/-57mj_DGR7A/s1600-h/pulsedrop.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 74px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/Svt1HibotRI/AAAAAAAAAUo/-57mj_DGR7A/s320/pulsedrop.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5403040950323295506" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/09audio/pulsedrop.wav"&gt;pulsedrop.wav&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;The second is seeded initially with a sine wave, and higher frequencies emerge over time.&lt;br/&gt;&lt;br/&gt;I've been using my C audio library, "bcaudio", for these experiments. Writing psuedo-object-oriented C isn't that bad. It's fun to write code that will run very quickly. &lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://neednom.com/bfisher/blog/09audio/bcaudio.tgz"&gt;bcaudio.tgz&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1732021966570170897?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1732021966570170897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1732021966570170897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1732021966570170897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1732021966570170897'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/11/audio-noise.html' title='Audio: noise'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/Svt1DoPE9eI/AAAAAAAAAUg/RWfaBFIO_7A/s72-c/out_r.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3585212174080627657</id><published>2009-05-10T01:53:00.000-07:00</published><updated>2010-08-31T21:19:36.758-07:00</updated><title type='text'>Simple image output</title><content type='html'>Sometimes, it can be nice to have a dead-simple format for bitmap images. If I'm writing some low level code (say, in C) dealing with graphics, at times I will want a quick way to 
quickly output an image file. Let's say that I'm too lazy to bring in a graphics library that can write actual .pngs .  The .bmp format, while simple, is a bit more complicated than a list of rgb values, because of alignment. 
&lt;br/&gt;&lt;br/&gt;
I wrote a little program that converts ".simple" images (which are a list of R,G,B bytes) into 24 bit .bmp files.
So, your program simply writes a tiny header and three bytes per pixel, and simpletobmp.exe turns this into a .bmp.&lt;br/&gt;&lt;br/&gt;
This works well from Python, too, when the PIL isn't around.&lt;br/&gt;&lt;br/&gt;
Here is an example of how to draw a ".simple" image:
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;FILE * fout;
fout = fopen(&amp;quot;test.simple&amp;quot;,&amp;quot;wb&amp;quot;);
fputc('S', fout);
fputc('2', fout);
fputc('4', fout);
int x,y,width,height;
width = 512; height = 256;
fwrite(&amp;amp;width,sizeof(int), 1, fout); 
fwrite(&amp;amp;height,sizeof(int), 1, fout); 

for (y=0; y&amp;lt;height; y++)
{
  for (x=0; x&amp;lt;width; x++)
  {
    fputc( y%256 , fout); //Red
    fputc( x%256 , fout); //Green
    fputc( 0 , fout);          //Blue
  }
}
fclose(fout);
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;&lt;br/&gt;
Or, in Python,
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;
import array
fout = open('pyout.simple', 'wb')

chars = array.array('c') #char
chars.append('S')
chars.append('2')
chars.append('4')
chars.tofile(fout)

WIDTH=512; HEIGHT=256
ints = array.array('l') #signed long
ints.append(WIDTH)
ints.append(HEIGHT)
ints.tofile(fout)

bytes = array.array('B') #unsigned char
for y in range(HEIGHT):
 for x in range(WIDTH):
  bytes.append(y%256)
  bytes.append(x%256)
  bytes.append(0)

bytes.tofile(fout)
fout.close()
&lt;/code&gt;&lt;/pre&gt;
(This one draws a gradient in red and green. Change the fputc lines in the inner loop to draw the image you want. A common usage is to set up a 2d array of pixels, draw the picture into that array, and then output everything to a file).
&lt;br/&gt;&lt;br/&gt;
Now, one can run
&lt;pre&gt;
simpletobmp.exe o test.simple test.bmp
&lt;/pre&gt;
to get the image.
&lt;br/&gt;&lt;br/&gt;
This is very similar to how in Linux, one can write a &lt;a href="http://en.wikipedia.org/wiki/Portable_pixmap"&gt;.ppm&lt;/a&gt; file, which is, literally, a brief header and list of rgb values. 
The .ppm format even accepts pixel information in human-readable ascii digits! Sounds ridiculous, but this type of thing can be useful.
&lt;br/&gt;&lt;br/&gt;
Download: &lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/mayimg/simpletobmp.zip"&gt;Windows binary&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/mayimg/simpletobmp_src.zip"&gt;Source&lt;/a&gt; LGPL license.&lt;br/&gt;&lt;br/&gt;

Simpletobmp uses the LGPL bmp_io library by John Burkardt.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3585212174080627657?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3585212174080627657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3585212174080627657' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3585212174080627657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3585212174080627657'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/05/simple-image-output.html' title='Simple image output'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7140275302631401939</id><published>2009-05-10T00:12:00.000-07:00</published><updated>2010-08-31T21:20:28.278-07:00</updated><title type='text'>Bit packing preprocessor</title><content type='html'>When working at lower-levels, sometimes it pays to conserve bits. Binary formats sometimes use all of the bits they are given - when I was working with MIDI, I came across this a lot. (If you have a full int, you might as well use all 32 of those guys.)
&lt;br/&gt;&lt;br/&gt;
On a more practical note, 16bit color is often 5bits red, 6bits green, and 5bits blue. A color is stored as a 16 bit integer in the format rrrrrggggggbbbbb, 
where each letter represents a bit. 
It isn't difficult to write code to extract these bit fields, but to make it faster and more readable, I wrote some Python to write the C for me.
The Python script takes a string like "00rrr000" and outputs C code with the appropriate shifts and masks.
&lt;br/&gt;&lt;br/&gt;
Bit tools. Example usage:
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;gt; necessarybits(640)
10 bits are required to store values up to 640
2 ** 10 = 1024

&amp;gt; print tobinary(46)
00101110

&amp;gt; print frombinary('1100_1100') 
204

&amp;gt; pattern('00rrr000', True)
  r is a 3bit number
  Packing:
  assert(r&amp;lt;8);
  unsigned char packed &amp;#124;= r&amp;lt;&amp;lt;3;
  Unpacking:
  unsigned char r = (packed &amp;amp; 0x3f)&amp;gt;&amp;gt;3; //packed &amp;amp; 0b00111111

&amp;gt; pattern('00000bbb')
  b is a 3bit number
  Packing:
  unsigned char packed &amp;#124;= b;
  Unpacking:
  unsigned char b = packed &amp;amp; 0x7; //packed &amp;amp; 0b00000111
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;&lt;br/&gt;
I find "00rrr000" to be a lot clearer than '(packed &amp;amp; 0x3f)&amp;gt;&amp;gt;3' .
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/mayimg/bit_tools.py"&gt;Python source&lt;/a&gt;&lt;br/&gt;
(34 lines of quick/unpretty code)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7140275302631401939?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7140275302631401939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7140275302631401939' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7140275302631401939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7140275302631401939'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/05/bit-packing-preprocessor.html' title='Bit packing preprocessor'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7843815902012149021</id><published>2009-03-31T23:31:00.000-07:00</published><updated>2009-04-01T01:34:23.167-07:00</updated><title type='text'>Midi (part 4)</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://b3nf.com/codepages/bmidi/mtowave/"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 156px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/Sc3JwjOVuzI/AAAAAAAAAUI/Eugy8_tV5o8/s320/ss01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5318128570920713010" /&gt;&lt;/a&gt;

Today I released a new project, &lt;a href="http://b3nf.com/codepages/bmidi/mtowave/"&gt;bmidi to wave&lt;/a&gt;.


&lt;p&gt;It can be used to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Turn midi files into great-sounding wav and mp3 files.&lt;/li&gt;
&lt;li&gt;Play midi files, especially if you are unsatisfied with your current midi out quality.&lt;/li&gt;
&lt;li&gt;Get information about SoundFont files and preview their voices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find high quality SoundFonts online, and play each instrument in the midi with the SoundFont of your choice,
resulting in the best possible sound. Use the mixer to fine-tune the volume and pan of each channel. Also, you
can see a score view of notes in a particular track.&lt;/p&gt;

&lt;p&gt;It is essentially a frontend for Timidity, the program that does the actual playback. Instead of having to edit the configuration files by hand, though, there is a gui.&lt;/p&gt;

&lt;p&gt;This is a program I've been meaning to write for a long time. I first had the idea and initial designs in 2002, when midi files were more common. It didn't take very long to write.&lt;p&gt;

&lt;p&gt;Process management in Python is not too bad thanks to the subprocess module, which has a good interface. I also learned about Python threads through this project, primarily because one wants a responsive GUI while the song is playing and the time slider is moving. &lt;/p&gt;

&lt;p&gt;Surprisingly, one of the more complicated parts of the program was allowing playback to start other places in the song. Timidity doesn't do this,
and so I have to create a temporary truncated midi file for it to play. I would just chop off all events before a certain time, but because events like instrument change can occur at any time, all of the instruments would be wrong, let alone tempo and pitch bend.
Tempo changes can occur at any time, and so it is not simple to correlate a midi tick with clock time. 
&lt;/p&gt;

&lt;p&gt;Midi is a compact, but pretty well-designed format. Not many binary formats from 1982 are around today.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7843815902012149021?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7843815902012149021/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7843815902012149021' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7843815902012149021'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7843815902012149021'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/04/midi-part-4.html' title='Midi (part 4)'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_sIga-_J94mI/Sc3JwjOVuzI/AAAAAAAAAUI/Eugy8_tV5o8/s72-c/ss01.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3485612213721675506</id><published>2009-03-31T23:24:00.000-07:00</published><updated>2010-08-31T21:21:20.009-07:00</updated><title type='text'>MS Paint Animation</title><content type='html'>I gave MS Paint the ability to make animations and save them as .avi files.
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://neednom.com/bfisher/blog/flipbook/movie.html"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 183px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SdMk5AqZjII/AAAAAAAAAUQ/b3wYJ4EVZEE/s320/ss.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5319636146703666306" /&gt;&lt;/a&gt;
&lt;br/&gt;
I wanted to write a "flipbook" program, where you could draw a series of frames and create a simple animation. 
I think this type of program could be fun for kids. 
&lt;br/&gt;&lt;br/&gt;
I was about to write the program in Pygame, but found myself re-implementing many of the standard bitmap editing tools. It's easy to make a rectangle and oval tool, but I didn't really feel like making the fill tool or selection tool. 
So, instead, I used MSPaint as part of the interface to the program. (This is completely a hack, and the resulting program isn't robust, but it was kind of interesting to do). 
&lt;a href="http://neednom.com/bfisher/blog/flipbook/movie.html"&gt;See a video&lt;/a&gt; of how to use it - you can move from frame to frame, duplicate the current frame, and play the animation.
&lt;br/&gt;&lt;br/&gt;

I'm trying to make this look like just one program. A lot is going on behind the scenes. 
The program is a c# app that, first, launches Paint. It has a window style that causes it to be on top of other windows. 
The program uses many Windows API SendKey calls to send key events to Paint. When you move from one frame to the next, it does the following:
&lt;ul&gt;
&lt;li&gt;Tell paint to select all (Ctrl A), and cut (Ctrl X)&lt;/li&gt;
&lt;li&gt;Take that image from the clipboard and save it to a .png file&lt;/li&gt;
&lt;li&gt;Open the next .png file in memory and put it in clipboard&lt;/li&gt;
&lt;li&gt;Tell paint to paste (Ctrl V) and deselect (Esc)&lt;/li&gt;
&lt;/ul&gt;
The other operations are done with a similar series of events. I had to tune the timing; the c# program sleeps while waiting for Paint. 
The play preview actually opens up a borderless c# window that is positioned so that it appears above the image, and cycles through the images.
&lt;br/&gt;&lt;br/&gt;
It ended up working. The Win api gives you almost too much to play with. Now I know that c# apps can send simulated keystrokes to other processes, for semi-practical purposes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3485612213721675506?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3485612213721675506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3485612213721675506' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3485612213721675506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3485612213721675506'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/04/ms-paint-animation.html' title='MS Paint Animation'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_sIga-_J94mI/SdMk5AqZjII/AAAAAAAAAUQ/b3wYJ4EVZEE/s72-c/ss.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5109892469246046014</id><published>2009-03-20T22:05:00.000-07:00</published><updated>2010-08-31T21:22:00.727-07:00</updated><title type='text'>Fishsquish</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/ScR1yD4aUPI/AAAAAAAAAT4/XL0tkuTTuZI/s1600-h/f1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 254px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/ScR1yD4aUPI/AAAAAAAAAT4/XL0tkuTTuZI/s320/f1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5315502963099848946" /&gt;&lt;/a&gt;
I made an arcade-style game. You have to quickly out-maneuver your enemies and push blocks to squish them. 
&lt;br/&gt;&lt;br/&gt;
I was influenced by an old Mac shareware game, but I've added some twists. The blocks have numbers, and if you can add to 15, it will stun your enemies and give you points.
&lt;br/&gt;&lt;br/&gt;
The game is written using Pygame. I've also ported it to the &lt;a href="http://en.wikipedia.org/wiki/One_Laptop_per_Child"&gt;One Laptop Per Child&lt;/a&gt; XO. 
&lt;br/&gt;&lt;br/&gt;
I'm going to replace the graphics and sound effects; the ones there now are essentially placeholders. I'll also rewrite some of the levels.
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/ScR1_GlFbfI/AAAAAAAAAUA/JX97Utkd4FM/s1600-h/f3.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 254px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/ScR1_GlFbfI/AAAAAAAAAUA/JX97Utkd4FM/s320/f3.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5315503187162394098" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/squish_0_95.zip"&gt;Download&lt;/a&gt;. 
To play it, you need a recent version of Python, and for Pygame to be installed. (Sorry, if you don't already have Pygame you'll have to &lt;a href="http://www.pygame.org/download.shtml"&gt;get&lt;/a&gt; it.) Works in Windows and Linux. &lt;br/&gt;&lt;br/&gt;
When the game is more complete I'll post a Windows binary.
&lt;br/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5109892469246046014?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5109892469246046014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5109892469246046014' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5109892469246046014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5109892469246046014'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/03/fishsquish.html' title='Fishsquish'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_sIga-_J94mI/ScR1yD4aUPI/AAAAAAAAAT4/XL0tkuTTuZI/s72-c/f1.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-21704655039202242</id><published>2009-02-22T21:15:00.000-08:00</published><updated>2010-08-31T21:26:05.424-07:00</updated><title type='text'>Midi (part 3) Tunescript</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://b3nf.com/codepages/bmidi/"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 309px; height: 320px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/SaIw_TduGYI/AAAAAAAAATw/Z-Gr3dko6wo/s320/tunescriptshot1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5305857175110687106" /&gt;&lt;/a&gt;

Here is another midi project done in my spare time. Tunescript is a musical toy, where you can enter a list of notes to create a song. 
Unlike other interfaces like this, though, tunescript supports a lot of features like multiple tracks and instruments, chords, percussion, accented notes, and even pitch bends.
Also, I put much thought into the syntax.
&lt;br/&gt;&lt;br/&gt;
For a lot of information, many examples, and to download, visit &lt;a href="http://b3nf.com/codepages/bmidi/"&gt;this page&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
It is pretty fun to invent a domain specific language. Because the language doesn't (yet) have nested constructs, I don't need full parsing. I came up with a nice way to interpret the input. It works kind of like a finite state machine that is receiving a stream of instructions. For example, the character 'b' can mean either flat, as in 'Ab', or the note 'b', but there is no ambiguity, because the symbol 'A' causes a transition to a state that can accept the 'b'.
Adding multiple tracks ended up being pretty simple, because I just use simple string operations to split the tracks.
&lt;br/&gt;&lt;br/&gt;
In more detail, the core of my interpreter looks something like this. The use of the while loop isn't very good and should probably be made into a for loop, but for some reason I was thinking of gotos, perhaps because of the underlying finite-state-machine influence.&lt;br/&gt;&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;...main loop...
  while s!='':
    result, s = self.pullFullNote(s, track)
    if result: continue
    
    result, s = self.pullFullNoteSet(s, track)
    if result: continue
    
    result, s = self.pullFullModOctave(s, track)
    if result: continue
    
    #if i get here, i couldn't interpret something, throw an error.
  
def pullFullNote(self, s, track):
  if it is not a note,
    return False, s
  
  #otherwise, consume some of the characters from s
  next_s = s[2:]
  
  #add the note to the track
  self.trackobjects[track].addnote()...
  
  return True, next_s

&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;&lt;br/&gt;
What is nice is that this pattern can be followed repeatedly on smaller levels. The pullFullNote() can call pullPitch() or pullVolumeDuration() in just the same way, and those can themselves call pull functions. If pullVolumeDuration() doesn't see a match, it simply returns False with the original string given. Essentially, the benefit is that there is no need to be explicitly asking "can the next thing be a note?", because the pullNote style functions will smoothly drop through when the next thing is not a note. I don't think I'm explaining this well, but it is all there in interpretsyntax.py if you are interested.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-21704655039202242?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/21704655039202242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=21704655039202242' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/21704655039202242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/21704655039202242'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/02/midi-part-3-tunescript.html' title='Midi (part 3) Tunescript'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/SaIw_TduGYI/AAAAAAAAATw/Z-Gr3dko6wo/s72-c/tunescriptshot1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4031182634319334711</id><published>2009-02-22T20:42:00.000-08:00</published><updated>2010-08-31T21:25:03.944-07:00</updated><title type='text'>Midi (part 2) Scoreview</title><content type='html'>For fun, I wrote a program that visualizes the contents of Midi files.
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://b3nf.com/codepages/bmidi/"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 287px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SaIps_Cz_qI/AAAAAAAAATo/8ULipCjBzdE/s320/scoreviewsc2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5305849163810078370" /&gt;&lt;/a&gt;

The "score view" is just a Tkinter canvas on which lines and ovals are drawn. None of the graphics are bitmaps (except the clefs), which means that there is freedom to quickly zoom in and out. The sharp signs are actually the text "#" drawn at that point. Writing a custom coordinate translation made this code so much easier. When I specify y coordinates, they are given in units where 2 units is the height of between staff lines. So, moving a note up or down just means incrementing or decrementing its position, and only the lowest level of code needs to know about the actual pixel coordinates.
&lt;br/&gt;&lt;br/&gt;
This tool can be a useful way to explore the contents of a midi song. Besides showing the score for a track, it can also get channel information:
&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://b3nf.com/codepages/bmidi/"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 217px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/SaIpjI0z-gI/AAAAAAAAATY/j7NN1cFXAyQ/s320/scoreviewm.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5305848994637019650" /&gt;&lt;/a&gt;
&lt;br/&gt;
Also, one can view all of the midi events in a track, in a human-readable format:
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://b3nf.com/codepages/bmidi/"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 272px; height: 320px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/SaIpmcqng0I/AAAAAAAAATg/iEXYVFnE8cE/s320/scoreviewlist.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5305849051502576450" /&gt;&lt;/a&gt;
&lt;br/&gt;
For a lot of information, and to download, visit &lt;a href="http://b3nf.com/codepages/bmidi/"&gt;this page&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
If you've been wondering, yes, the eventual goal is to create a midi editor. This project isn't high priority, though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4031182634319334711?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4031182634319334711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4031182634319334711' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4031182634319334711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4031182634319334711'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/02/midi-part-2-scoreview.html' title='Midi (part 2) Scoreview'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_sIga-_J94mI/SaIps_Cz_qI/AAAAAAAAATo/8ULipCjBzdE/s72-c/scoreviewsc2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1903043774780764089</id><published>2009-02-15T21:41:00.000-08:00</published><updated>2010-08-31T21:28:12.097-07:00</updated><title type='text'>Midi (part 1)</title><content type='html'>I've been playing around with &lt;a href="http://en.wikipedia.org/wiki/MIDI"&gt;MIDI&lt;/a&gt; files lately.
&lt;br/&gt;&lt;br/&gt;

MIDI files are essentially a list of notes with associated timing, and in the 90s were popularly used to make and share music. They can be easily made with a synthesizer and a computer. A drawback, though, is that the songs sound different if played on different computers. For example, different sound cards have different interpretations of what the "acoustic grand piano" voice should sound like, and the midi file itself does not contain the audio sample data. However, midis can be programmatically generated, and one can change tempo and pitch without losing quality. After broadband internet and mp3s became common, most people stopped using MIDI, although it still finds a use in music notation programs.
&lt;br/&gt;&lt;br/&gt;

Unfortunately, these days, new computers come with awful-sounding software MIDI instruments, creating the false impression that "MIDI" is synonymous with "cheap, bad audio quality." In fact, there is nothing inherently bad-sounding about MIDI music, and with a set of good SoundFonts, it can sound very good.
&lt;br/&gt;&lt;br/&gt;
Anyways, here are &lt;a href="http://neednom.com/bfisher/blog/midi/part1/bbuilder.zip"&gt;some Python scripts&lt;/a&gt; that can be used to create midis. The interface is not the best, but it was very quick to implement. Examples:

&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;b = BMidiBuilder()
b.note('c', 1) 
#the 1 means the note's length is 1 qtr note
b.note('d', 1)
b.note('e', 1)
b.note('f', 1)
b.note('g', 4)
b.save('out.mid')
&lt;/code&gt;&lt;/pre&gt;

This plays part of a scale. To play a chord, "rewind" can be used to go back and lay more notes on top of existing notes.

&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;
b = BMidiBuilder()
b.note('c#', 1)
b.rewind(1)
b.note('f', 1)
b.rewind(1)
b.note('g#', 1)
b.save('out.mid')
&lt;/code&gt;&lt;/pre&gt;

Two tracks can be joined, to make a little tune:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;tr1 = BMidiBuilder()
tr1.setInstrument('acoustic bass')
tr1.note('c3', 2)
tr1.note('d3', 2)
tr1.note('e3', 2)
tr1.rest(2)
tr1.note('f3',2)

tr2 = BMidiBuilder()
tr2.setInstrument('ocarina')
tr2.note('e4', 2)
tr2.note('f4', 2)
tr2.note('g4', 2)
tr1.rest(2)
tr1.note('a4',2)

bbuilder.joinTracks( [tr1, tr2], 'out.mid')

&lt;/code&gt;&lt;/pre&gt;

Also, there are a set of classes in bmidilib.py that can build a midi file and any type of event, like percussion, modulation, metadata, or pitch bends.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/midi/part1/bbuilder.zip"&gt;Download&lt;/a&gt;, tested in Python 2.5

&lt;br/&gt;&lt;br/&gt;
Tonight I wondered what it would sound like to play the same note on the 127 general midi instruments. The result sounds interesting.

&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;b = bbuilder.BMidiBuilder()
b.tempo = 400
for i in range(127):
  #insert a raw instrument change event
  evt = bmidilib.BMidiEvent()
  evt.type='PROGRAM_CHANGE'
  evt.channel=1
  evt.data = i
  b.insertMidiEvent(evt)
  b.note('c3',0.4)
b.save('cool.mid')
&lt;/code&gt;&lt;/pre&gt;

&lt;a href="http://neednom.com/bfisher/blog/midi/part1/allinstruments.mid"&gt;Hear it&lt;/a&gt;. 
&lt;br/&gt;
(Sound depends on your midi device, I don't recommend Quicktime so you may find it better to download this file and open it elsewhere).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1903043774780764089?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1903043774780764089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1903043774780764089' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1903043774780764089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1903043774780764089'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/02/midi-part-1.html' title='Midi (part 1)'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-118189092313890673</id><published>2009-02-01T18:22:00.000-08:00</published><updated>2009-02-01T18:26:07.493-08:00</updated><title type='text'>Minimath</title><content type='html'>Here is a long-term project of mine that still isn't finished, but is starting to become useful. I started this in a computer science class last year, so not all of it was done on my own time.  At Olin, sometimes I'd like to have the software equivalent of my TI calculator. Matlab can be useful, but takes 
a while to load and cannot be described as light-weight. 
So, for quick calculations, I made Minimath, a small command line interface to evaluate math. 
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/SYZY-TGKSmI/AAAAAAAAATQ/XmvIRmL06tM/s1600-h/shot2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 218px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/SYZY-TGKSmI/AAAAAAAAATQ/XmvIRmL06tM/s320/shot2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5298019838949214818" /&gt;&lt;/a&gt;

The main design goal was to save typing for commonly-occuring tasks. As you can see, the UI is pretty sparse now, and that is on my list of things to improve.

&lt;h4&gt;Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Fills-in incomplete expressions. It is perfectly ok to evaluate "sin(pi" without the closing paren, because it will understand what you meant.&lt;/li&gt;
&lt;li&gt;Line history, with arrow keys. Control-Up and Control-Down search history based on prefix like Matlab.&lt;/li&gt;
&lt;li&gt;Supports complex numbers, saving values in variables. &lt;/li&gt;
&lt;li&gt;You can press Alt-. to create a "-&gt;" symbol to store values, like a TI 83.&lt;/li&gt;
&lt;li&gt;"ans" refers to last result.&lt;/li&gt;
&lt;li&gt;If the first key you press is an operator, it fills in the "ans" like a TI 83. This one saves a lot of time.&lt;/li&gt;
&lt;li&gt;Any valid Python expression can also be evaluated.&lt;/li&gt;

&lt;li&gt;Pressing the "\" key (while not in a string literal) creates a lambda symbol, so that expressions like "f = λx.x+2" can be used.
&lt;li&gt;Pretty print, which behind-the-scenes is parsing the expression, creating and rendering a temporary LaTeX file.&lt;/li&gt;
&lt;li&gt;Uses Numpy library which provides many math functions.&lt;/li&gt;
&lt;/ul&gt;
Another feature is variable substitution. Pressing alt-, makes a "&lt;-" symbol that can be used to subsitute values into an expression. For example,
the result of "2*x^2 + 3*x &lt;- 4" is 44.

&lt;h4&gt;Added Syntax&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The symbol "^" now is exponentiation.&lt;/li&gt;
&lt;li&gt;The ternary expression ? : can be used as in C.&lt;/li&gt;
&lt;li&gt;The shortened "for(i,5)" can be used in place of "for i in range(5)".&lt;/li&gt;
&lt;li&gt;Syntax for making arrays: "arr = a[1 2 3]" and "arr = a[1 2 3;4 5 6]". (Note that arrays are different than Python lists).&lt;/li&gt;
&lt;li&gt;"i[0,4,10]" is like Matlab's linspace, 10 elements equally spaced from 0 to 4.&lt;/li&gt;
&lt;li&gt;"i[0..4]" is an inclusive range, the result is [0,1,2,3,4].&lt;/li&gt;
&lt;li&gt;It allows more items on one line than Python, just use semicolons. In particular, something like "for val in array:t+=val;print val" will work all on one line.&lt;/li&gt;

&lt;/ul&gt;

In the future, I plan to add more-advanced interpretation. 
For example, the string "3x4" could be recognized as "3*x^4", and "4(x+3)" to "4*(x+3)"
Also, I should integrate Matplotlib or something similar.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-118189092313890673?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/118189092313890673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=118189092313890673' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/118189092313890673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/118189092313890673'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/02/minimath.html' title='Minimath'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/SYZY-TGKSmI/AAAAAAAAATQ/XmvIRmL06tM/s72-c/shot2.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2105118607949565787</id><published>2009-01-23T23:29:00.001-08:00</published><updated>2010-08-31T21:29:12.073-07:00</updated><title type='text'>AutoShoop</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/SXrDZjcCBPI/AAAAAAAAASo/Qmwnl-_Z-AE/s1600-h/180px-Photoshoop.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 180px; height: 192px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/SXrDZjcCBPI/AAAAAAAAASo/Qmwnl-_Z-AE/s400/180px-Photoshoop.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5294759155704399090" /&gt;&lt;/a&gt;
&lt;br/&gt;
An alternative to developing new technology is to apply existing technology in a new way. I was thinking about face recognition software a few days ago, and thought of a fun application (this isn't a serious project at all, by the way). 
So I quickly wrote AutoShoop, that uses a face-detection algorithm to draw "shoop da woop" shapes on people's faces. In other words, it automatically turns this:
&lt;br/&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/SXrDp_xFtZI/AAAAAAAAASw/JapQPCZnchw/s1600-h/cassidy.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 123px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/SXrDp_xFtZI/AAAAAAAAASw/JapQPCZnchw/s320/cassidy.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5294759438186821010" /&gt;&lt;/a&gt;

into this:
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_sIga-_J94mI/SXrDumRK27I/AAAAAAAAAS4/jAx24kxn-tI/s1600-h/cassidy_shoop.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 134px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/SXrDumRK27I/AAAAAAAAAS4/jAx24kxn-tI/s320/cassidy_shoop.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5294759517241400242" /&gt;&lt;/a&gt;


This works automatically - no Photoshop needed! &lt;!--It can also use another meme, turning this:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/SXrD1nxAcgI/AAAAAAAAATI/icse6aMBp70/s1600-h/1978.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 230px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SXrD1nxAcgI/AAAAAAAAATI/icse6aMBp70/s320/1978.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5294759637902455298" /&gt;&lt;/a&gt;
into Sparta!

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/SXrDxuiFWCI/AAAAAAAAATA/xUcN1hWVCAo/s1600-h/1978.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 230px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/SXrDxuiFWCI/AAAAAAAAATA/xUcN1hWVCAo/s320/1978.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5294759570999433250" /&gt;&lt;/a&gt;
--&gt;

I could have spent some time making the results better, but that's not really the point. It's more of a proof of concept.

&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/autoshoop.zip"&gt;autoshoop.zip&lt;/a&gt; (Windows GUI binary and sample images)&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/autoshoopsrc.zip"&gt;autoshoopsrc.zip&lt;/a&gt; (C# source)
&lt;br/&gt;&lt;br/&gt;
I didn't write any of the face-detection part; I simply used &lt;a href="http://www.kyb.mpg.de/bs/people/kienzle/fdlib/fdlib.htm"&gt;fdlib&lt;/a&gt;. 
Your results may vary; I found that for some reason fdlib sometimes has a hard time finding faces in certain photos. When it works, though, it works pretty well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2105118607949565787?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2105118607949565787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2105118607949565787' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2105118607949565787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2105118607949565787'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/01/autoshoop.html' title='AutoShoop'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_sIga-_J94mI/SXrDZjcCBPI/AAAAAAAAASo/Qmwnl-_Z-AE/s72-c/180px-Photoshoop.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2167124905334364592</id><published>2009-01-14T17:21:00.000-08:00</published><updated>2009-01-14T17:43:44.665-08:00</updated><title type='text'>Strangeparse</title><content type='html'>Let's say I want to be able to parse an arbitrary expression like&lt;br/&gt;
&lt;pre&gt;f(f( a+f(a * b * f(c))))&lt;/pre&gt;&lt;br/&gt;
where a, b,c are variables, and f is some function. I want to make a parse tree, and possibly to transform the tree.
Last night I thought of a very unorthodox way to do this.
&lt;br/&gt;&lt;br/&gt;
I've heard of yacc/lex, pyparsing, and Python's ast. However, for what I have in mind this is all I need:&lt;br/&gt;
- Python supports overloading operators.&lt;br/&gt;
- Python has an "eval" for dynamically running code.
&lt;br/&gt;&lt;br/&gt;
My idea is to create Python objects for f, a, b,c, and then *eval* the expression as if it were Python code. The objects I create will have their operators overloaded so that a+b, for example, returns an expression like ['+', a, b].
Here's an example that supports addition, multiplication, and functions taking any number of arguments. (Adding the rest of the operators is simple).
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;class FunctionSymbol:
  def __init__(self, name):
    self.name = name
  def __call__(self, *args):
    return SubExp(self, list(args))
  def __str__(self):
    return 'FN_'+self.name

class VariableSymbol:
  def __init__(self, name):
    self.name = name
  def __add__(self, other):
    return SubExp('OP_ADD', [self, other])
  def __mul__(self, other): 
    return SubExp('OP_MULT', [self, other])
  def __str__(self):
    return 'VAR_'+self.name

class SubExp(VariableSymbol):
  def __init__(self, op, args):
    self.op = op; self.args= args
  def __str__(self):
    return str(self.op) + '(' + ','.join(map(str, self.args)) + ')'

def strangeparser(s):
  #parse something by evaluating it as if it were Python code
    
  symbols = {}
  #create objects for the symbols in the string 
  snospace = s.replace(' ','').replace('\t','') + ' '
  import re
  for match in re.finditer('[a-zA-Z_]+', snospace):
    strfound = match.group()
    if strfound not in symbols:
      #assume that if the next character is &amp;quot;(&amp;quot;, then it is a function
      if snospace[match.end()]=='(':
        symbols[strfound] = FunctionSymbol(strfound)
      else:
        symbols[strfound] = VariableSymbol(strfound)
  # evaluate it
  try: 
    return eval( s , globals(), symbols )
  except Exception, e: 
    print 'Could not parse. %s' % str(e)
    return None
  
def main():
  tree = strangeparser('a+b+c')
  print tree
  # OP_ADD(OP_ADD(VAR_a,VAR_b),VAR_c)
  tree = strangeparser('f(f( a+f(a * b * f(c))))')
  print tree
  # FN_f(FN_f(OP_ADD(VAR_a,FN_f(OP_MULT(OP_MULT(VAR_a,VAR_b),FN_f(VAR_c))))))
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;
Also, I've written code that turns this tree into a sequence of simple operations. Coincidentally, I think this could be used to write a compiler. I haven't read any books about that, though. &lt;br/&gt;
&lt;pre&gt;
input:
 f(f( a+f(a * b * f(c))))

output:
 i1=OP_MULT(a,b);
 i2=FN_f(c);
 i3=OP_MULT(i1,i2);
 i4=FN_f(i3);
 i5=OP_ADD(a,i4);
 i6=FN_f(i5);
 i7=FN_f(i6);
 return i7;
 &lt;/pre&gt;&lt;br/&gt;
I'm working on a way to conserve the temporary variables used here, because there is probably a way to reuse them after they aren't needed. What I do is go down to the bottom of the tree, find something that can be evaluated, and replace that deepest node with a temporary variable. I then repeat that process until the whole tree has been "flattened".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2167124905334364592?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2167124905334364592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2167124905334364592' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2167124905334364592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2167124905334364592'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/01/strangeparse.html' title='Strangeparse'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6466577952097846270</id><published>2009-01-01T13:06:00.000-08:00</published><updated>2009-01-01T13:13:10.317-08:00</updated><title type='text'>Homepage</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.b3nf.com"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 281px;" src="http://1.bp.blogspot.com/_sIga-_J94mI/SV0wcnoLkdI/AAAAAAAAASc/GF92UP2VY7Y/s320/still.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5286434805834813906" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
On the internet, I now have a place that I can call my home. I've uploaded an initial version of a personal website. 
&lt;br/&gt;&lt;br/&gt;
It can be seen at &lt;a href="http://b3nf.com"&gt;http://b3nf.com&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
(The section called "Recent Experiments" consists of links to this blog, but the other sections like Projects are new. There's a lot of content to explore.) 
&lt;br/&gt;&lt;br/&gt;
I've been working on this occasionally over the course of a few years, which is why the site doesn't seem very unified yet. 
&lt;br/&gt;&lt;br/&gt;
I intend to keep this blog active, though, so stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6466577952097846270?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6466577952097846270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6466577952097846270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6466577952097846270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6466577952097846270'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2009/01/homepage.html' title='Homepage'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_sIga-_J94mI/SV0wcnoLkdI/AAAAAAAAASc/GF92UP2VY7Y/s72-c/still.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5141259261143146677</id><published>2008-12-21T14:08:00.000-08:00</published><updated>2010-08-31T22:38:46.845-07:00</updated><title type='text'>Huffman coding</title><content type='html'>I've been playing with Scheme a bit. After hearing about Huffman compression in a lecture, I first wrote something in Python, but noticed that the recursion I used lent itself well to a functional language, especially where tail-calls were optimized. So I wrote this:
&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_sIga-_J94mI/SU7DqD2S8PI/AAAAAAAAASU/9xAvWnMncpI/s1600-h/hcode.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 290px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SU7DqD2S8PI/AAAAAAAAASU/9xAvWnMncpI/s320/hcode.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5282374540307132658" /&gt;&lt;/a&gt;
&lt;br/&gt;

I've used it to construct a table for 9000 symbols with no problems, so it works pretty well. Can be downloaded &lt;a href="http://neednom.com/bfisher/blog/scheme/hfinal.scm"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5141259261143146677?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5141259261143146677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5141259261143146677' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5141259261143146677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5141259261143146677'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/12/huffman-coding.html' title='Huffman coding'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_sIga-_J94mI/SU7DqD2S8PI/AAAAAAAAASU/9xAvWnMncpI/s72-c/hcode.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7953094125008521342</id><published>2008-11-15T22:39:00.000-08:00</published><updated>2010-08-31T21:30:16.200-07:00</updated><title type='text'>ISM, an intermediate level language</title><content type='html'>For our Computer Architecture class, a partner and I created a MIPS cpu in Verilog. This was a really cool project that deserves its own description, but for now I'll focus on something I made while testing the cpu. The cpu runs MIPS assembly code. Writing assembly is not bad for simple tests, but when I started to write more complicated programs, I got tired of remembering what I was using $s0 for and so on.
&lt;br/&gt;&lt;br/&gt;
So, just for fun, I quickly wrote a Python script to translate assembly-like code into assembly. The small language I designed is an intermediate between higher-level code and assembly. The syntax is more like a higher level language, assigning values with "a=b" or adding with "c = a+b". It's not a compiler, but doesn't really need to be, and with too much abstraction one would be better off writing in C anyway.
&lt;br/&gt;&lt;br/&gt;
The cool part about the project is how quickly and naturally the code turned out. It only took an hour to have a fully working version that understood the opcodes, and had useful error messages for syntax errors. The code just flowed quickly as I found ways to reuse code and simplify the problem.
&lt;br/&gt;&lt;br/&gt;
For example, I needed to split each line of code by tokens. I came up with a creative way to do this - Python has a built-in way to split a string based on whitespace, " 'a b c&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;d e'.split() " evaluates to "['a','b','c','d','e']". So in order to split on both + and - tokens and whitespace, I did "input=input.replace('+',' + ').replace('-',' - '); result=input.split()". By surrounding each occurrance of '+' with whitespace, now the string is split by both whitespace and tokens, so that a+b and a + b are both recognized as ['a','+','b']. (This could also be done by splitting on a regex, but that would be slightly more complicated).
&lt;br/&gt;&lt;br/&gt;
The first, one-hour version, understands code like:&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;//add numbers from 1 to 25 and put result in sum.

registers i, const_one,sum,highest
seti const_one=1
seti sum=0
seti highest=25
seti i=0
LOOP:
 add i=i+const_one
 add sum=sum+i
 bne (i!=highest) goto LOOP
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;
Then, later, I started to add more features. I added syntax shortcuts like a++, and simulated more types of branching even though our cpu only supported bne. I also added inference, where each line is checked to infer which operation you are trying to do. For example, if the line is "c=a+b" it is clear that you are doing an add instruction, so you don't have to type "add". Also, it now understands simple loops and if structures, translating them to assembly. It looks like Python if you squint.
&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;// find prime numbers from 2 to 100.
// Place the largest found in result.
registers i,tmp, base, limit, result

// clear memory
for i,300,0
  dataWordIndex[i] = zero
endfor

limit=100
for base,2,100
  // check if we've already seen this number come up
  tmp = dataWordIndex[base]
  (tmp != zero) goto CONTINUELOOP
  
  // check off multiples of this number.
  i=base
  INNERLOOP:
    i = i + base
    dataWordIndex[i] = one
    (i &amp;lt; limit) goto INNERLOOP
  
  CONTINUELOOP:
endfor

// put largest prime found into result
for i,100,2
  tmp = dataWordIndex[i]
  if (tmp==zero)
    result=i
    goto DONE
  endif
endfor

DONE:
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;
It was fun to come up with syntax for this. Branches are done with the unique, yet readable syntax "(a==b) goto Label". Array-like syntax is used for accessing data memory, like 'dataWordIndex[i] = b'. (dataWordIndex means that the index is automatically multiplied by four, so that it can be used as an array of 32 bit integers.)
&lt;br/&gt;&lt;br/&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/SR_AqvcAshI/AAAAAAAAANM/C8cRpdBSchc/s1600-h/ism.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 101px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/SR_AqvcAshI/AAAAAAAAANM/C8cRpdBSchc/s320/ism.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5269141929567760914" /&gt;&lt;/a&gt;
&lt;br/&gt;
When the script runs, it also prints out Verilog $display statements, which can be placed in the Verilog code to see the values of the registers. 
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ism.zip"&gt;Here&lt;/a&gt; is the code. ism_simple.py is the first version. I realize that it won't be useful unless you have a mips assembler and cpu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7953094125008521342?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7953094125008521342/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7953094125008521342' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7953094125008521342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7953094125008521342'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/11/ism-intermediate-level-language.html' title='ISM, an intermediate level language'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/SR_AqvcAshI/AAAAAAAAANM/C8cRpdBSchc/s72-c/ism.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4084014282045712643</id><published>2008-11-02T16:46:00.000-08:00</published><updated>2010-08-31T21:31:28.510-07:00</updated><title type='text'>Rickroll Detector</title><content type='html'>Some people evidently don't understand when a meme is dead, or deserves to die. Rickrolls have been around for far too long and have even been picked up by the mainstream media. It's just not really funny anymore.
&lt;br/&gt;&lt;br/&gt;
Today I was kind of bored and made this in a few hours. It's a system for checking if a link is a rickroll. You provide a URL, then some audio analysis is done, and the result is a plot showing whether or not it was a rickroll. What's nice is that it will also detect spin-offs like Barak-roll, &lt;a href="http://www.youtube.com/watch?v=LKY5vvDC7Bc"&gt;scary roll&lt;/a&gt;, and so on.
&lt;br/&gt;&lt;br/&gt;

How it works: &lt;br/&gt;
A &lt;a href="http://code.google.com/p/launchorz/"&gt;Launchorz&lt;/a&gt; script directs what happens.&lt;br/&gt;
First, your audio is muted automatically.&lt;br/&gt;
Then, behind the scenes, the url is opened in Firefox. The program Total Recorder is used to capture the audio played by the webpage.&lt;br/&gt;
The page is closed after 15 seconds of capturing audio.&lt;br/&gt;
The audio is saved as a wav file in a temporary location, and your audio is unmuted.&lt;br/&gt;
Then, a MATLAB script is used to analyze the audio.&lt;br/&gt;
The audio is normalized to approximately the same volume.&lt;br/&gt;
Then, the cross-correlation is taken between a reference Rickroll audio and the recorded audio.&lt;br/&gt;
If there are peaks in the cross-correlation above around 200, then it is likely the page contained a Rickroll.
&lt;br/&gt;&lt;br/&gt;
Results:
After adjusting of parameters in the MATLAB code, the results are pretty good.&lt;br&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 260px;" src="http://2.bp.blogspot.com/_sIga-_J94mI/SQ5KP1EqqZI/AAAAAAAAAM0/os7KfnA8uaI/s320/a_hit.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5264226650247834002" /&gt;

&lt;br/&gt;"Warning: rick-roll detected"
&lt;br&gt;&lt;br&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 258px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SQ5KXO8SpZI/AAAAAAAAANE/bdk3Sb3s-Tw/s320/not.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5264226777451111826" /&gt;
&lt;br/&gt;"Safe: no rick-roll detected"
&lt;br&gt;&lt;br&gt;

&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 253px;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SQ5KUdO9b5I/AAAAAAAAAM8/qJCpwzwBtm8/s320/kylie.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5264226729747902354" /&gt;
&lt;br/&gt;Kylie Minogue's "I Should Be So Lucky". 

&lt;br&gt;&lt;br&gt;


(The mostly-evenly spaced peaks correspond with the steady beat in the music.) The results would probably be even better if the comparison was done in frequency, instead of in time. But I'm not about to waste any more time than the few hours I put into this.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/rrdetect.zip"&gt;Here's&lt;/a&gt; the script I used but it needs Launchorz, MATLAB, and the shareware program Total Recorder.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4084014282045712643?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4084014282045712643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4084014282045712643' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4084014282045712643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4084014282045712643'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/11/rickroll-detector.html' title='Rickroll Detector'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_sIga-_J94mI/SQ5KP1EqqZI/AAAAAAAAAM0/os7KfnA8uaI/s72-c/a_hit.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4814028860536276846</id><published>2008-10-27T16:03:00.000-07:00</published><updated>2010-08-31T21:32:13.029-07:00</updated><title type='text'>Mailman Scraper</title><content type='html'>I wrote a script to scrape messages from mailing-list archives and store them in a MySql database. It can retrieve message, author, and date information for everything sent to the list.
&lt;br/&gt;&lt;br/&gt;
Code &lt;a href="http://neednom.com/bfisher/blog/mailman_scraper_php.zip"&gt;here&lt;/a&gt;. GPL, Requires PHP5 and mysql. You'll have to read README.txt for instructions.
&lt;br/&gt;&lt;br/&gt;
Intended for use with a Mailman list, one that looks something like:
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_sIga-_J94mI/SQZJCIJzbhI/AAAAAAAAAMs/_iUvlaSvOkk/s1600-h/mailman.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 206px;" src="http://4.bp.blogspot.com/_sIga-_J94mI/SQZJCIJzbhI/AAAAAAAAAMs/_iUvlaSvOkk/s320/mailman.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5261973515526172178" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Because this just uses regular expressions to scrape the pages, it's kind of fragile. It makes some assumptions about the format of the page and might not work with other versions of mailman.
&lt;br/&gt;&lt;br/&gt;
I've found this useful for a few projects. One result is a script that tracks list activity (and shows a flame-meter when the list gets intensely active; the current state is ranked from serene to mayhem). I'm also creating a site that allows searching the list archives, as well as voting on the quality of information and adding tags. Of course, one can also use this to create a Markov model of a list... but I'm not &lt;a href="http://en.wikipedia.org/wiki/Mark_V_Shaney"&gt;going&lt;/a&gt; &lt;a href="http://halfhourhacks.blogspot.com/2008/02/wordmash.html"&gt;there&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4814028860536276846?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4814028860536276846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4814028860536276846' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4814028860536276846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4814028860536276846'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/10/mailman-scraper.html' title='Mailman Scraper'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_sIga-_J94mI/SQZJCIJzbhI/AAAAAAAAAMs/_iUvlaSvOkk/s72-c/mailman.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6302156499608739046</id><published>2008-08-13T12:56:00.000-07:00</published><updated>2010-08-31T22:45:02.564-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Collect'/><title type='text'>Documentation Viewer</title><content type='html'>This project isn't very exciting, but it will be useful to me. I wrote a documentation viewer that displays documentation information in a tree control. 
&lt;br&gt;
&lt;a href="http://3.bp.blogspot.com/_sIga-_J94mI/SKM8xKlBCFI/AAAAAAAAAL8/edOWyBH_MlE/s1600-h/pyshot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SKM8xKlBCFI/AAAAAAAAAL8/edOWyBH_MlE/s400/pyshot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5234094007285516370" /&gt;&lt;/a&gt;
&lt;br&gt;
I spent a lot of time going through the Python library documentation, acquiring what I thought was important, and re-organizing all of the modules into my own categories. This also required writing Python scripts to turn reStructuredText, the loose and readable format of the Python documentation, into the xml format used by the doc viewer.
&lt;br&gt;
&lt;a href="http://2.bp.blogspot.com/_sIga-_J94mI/SKM82o3y5qI/AAAAAAAAAME/eXr8tADcdrM/s1600-h/cshot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_sIga-_J94mI/SKM82o3y5qI/AAAAAAAAAME/eXr8tADcdrM/s400/cshot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5234094101316691618" /&gt;&lt;/a&gt;
&lt;br&gt;
I also organized all of the documentation for C.
&lt;br&gt;&lt;br&gt;
One cool feature is that it can communicate with the SciTE code editor. So, when run from LnzScite, it appears like a tool window, and you can double-click a function in the browser, which will insert the function's name into the editor. (Watch for this in Launchorz 0.3). A planned feature is to be able to look up documentation on the currently selected function.
&lt;br&gt;
&lt;a href="http://2.bp.blogspot.com/_sIga-_J94mI/SKM89GhU_yI/AAAAAAAAAMM/Vu4F1nZGWh0/s1600-h/integ.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_sIga-_J94mI/SKM89GhU_yI/AAAAAAAAAMM/Vu4F1nZGWh0/s400/integ.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5234094212354735906" /&gt;&lt;/a&gt;

&lt;br&gt;
I wanted this program to feel small and lightweight, so I did this: the nodes are created dynamically, from the xml, when you expand a parent node. This means that the entire documentation isn't loaded into memory and no unneeded TreeNodes are created. I also spent time tuning the xml-parsing code to be very fast, using C#'s stream reader which avoids DOM overhead.
&lt;br&gt;&lt;br&gt;
You can download the Lnz Doc Viewer &lt;a href="http://neednom.com/bfisher/blog/jmn/docviewer_lnz.zip"&gt;here&lt;/a&gt;, which includes documentation for C, Python, and LnzScript (unfortunately only for Windows). The source, as usual, can be found from the &lt;a href="http://code.google.com/p/launchorz/"&gt;project page&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;

As an aside, I'm starting to take advantage of some of the benefits of xml. In the source for Launchorz, there are special comments for the provided functions. Everything starting with /// is treated as a documentation string, and can be processed by a Python script into an xml file. This xml file contains the prototypes and docs for every function in lnzscript - and the same file is used by a c# program, a c++ program, and a wsh script. And it doesn't stop there - if you open the same xml file in a web browser, it nicely &lt;a href="http://b3nf.com/codepages/lnz/reference/lnz_04_documentation.xml"&gt;renders&lt;/a&gt; the documentation with xslt!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6302156499608739046?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6302156499608739046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6302156499608739046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6302156499608739046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6302156499608739046'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/08/documentation-viewer.html' title='Documentation Viewer'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_sIga-_J94mI/SKM8xKlBCFI/AAAAAAAAAL8/edOWyBH_MlE/s72-c/pyshot.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3769716737898427014</id><published>2008-08-08T13:42:00.000-07:00</published><updated>2010-08-31T22:37:51.964-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>AI Scheme</title><content type='html'>Just for fun, a few days ago I wrote a minimax AI algorithm in Scheme. This was mainly a way to become more familiar with writing in Scheme, and so I wrote it from scratch. I had written pieces of Scheme a year ago but wanted to write something more practical. Writing in a functional language is fun because it pushes you to see problems from a new perspective.  (For those unfamiliar with the concept, of the code I wrote there are no variable assignments, for loops, while loops, or C-like arrays). Also, when finished, I was able to format the code nicely.
&lt;br&gt;&lt;br&gt;
&lt;a href="http://3.bp.blogspot.com/_sIga-_J94mI/SJy9dn5qiII/AAAAAAAAAL0/oGvvkdwYJ6I/s1600-h/scheme.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_sIga-_J94mI/SJy9dn5qiII/AAAAAAAAAL0/oGvvkdwYJ6I/s400/scheme.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5232265183722375298" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
To use this algorithm, you can provide definitions for create_new_table,get_open_spaces,place_at_position, and is_winning, and that will be all you need. This &lt;a href="http://neednom.com/bfisher/blog/scheme/minimax_scheme.zip"&gt;zip file&lt;/a&gt; contains the complete program and an example game implementation.

&lt;br&gt;&lt;br&gt;

If I get tired of S-expression syntax, Javascript is actually a good language for functional programming. It has the ability to do concisely express things like function() { call_something(); return function(){ do_something(); } }();  or function adder(x) { return function(y) {print('!'); return x+y}; } f = adder(1); f(1);. In fact, it did not take very much work for all of the &lt;i&gt;The Little Schemer&lt;/i&gt; examples to be written in JavaScript; see Douglas Crockford's &lt;a href="http://www.crockford.com/javascript/little.html"&gt;page&lt;/a&gt; and &lt;a href="http://www.crockford.com/javascript/little.js"&gt;this&lt;/a&gt;. Python's lambda, on the other hand, is intentionally limited to containing one expression, and there not seem to be any convenient way to create an anonymous inner function with scope.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3769716737898427014?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3769716737898427014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3769716737898427014' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3769716737898427014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3769716737898427014'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/08/ai-scheme.html' title='AI Scheme'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_sIga-_J94mI/SJy9dn5qiII/AAAAAAAAAL0/oGvvkdwYJ6I/s72-c/scheme.gif' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2249157647995448887</id><published>2008-08-04T20:12:00.000-07:00</published><updated>2010-08-31T21:34:16.532-07:00</updated><title type='text'>Version 0.2!</title><content type='html'>&lt;a href="http://code.google.com/p/launchorz/"&gt;Launchorz 0.2&lt;/a&gt;
&lt;br&gt;
&lt;a href="http://b3nf.com/codepages/lnz/"&gt;Documentation&lt;/a&gt;&lt;br&gt;&lt;br&gt;

&lt;a href="http://b3nf.com/codepages/lnz/"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/SJfKd7pEggI/AAAAAAAAALk/Vr8OTmUlq7c/s320/lnz02ss01.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5230872107789091330" /&gt;&lt;/a&gt;

I've just released version 0.2 of Launchorz. It comes with significantly more features, and even a dedicated code editor. If you're using Windows, it will save you a lot of time. Try it!

&lt;br/&gt;&lt;br/&gt;The code editor is a custom build of Scite. I've added autocomplete and tooltips to make it faster to write scripts.
&lt;br/&gt;&lt;br/&gt;

&lt;a href="http://b3nf.com/codepages/lnz/lnz_04_examples.html"&gt;See screencasts here&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="http://code.google.com/p/launchorz/"&gt;Download it here&lt;/a&gt; (under featured downloads)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2249157647995448887?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2249157647995448887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2249157647995448887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2249157647995448887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2249157647995448887'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/08/version-02.html' title='Version 0.2!'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_sIga-_J94mI/SJfKd7pEggI/AAAAAAAAALk/Vr8OTmUlq7c/s72-c/lnz02ss01.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3357566084614800510</id><published>2008-07-10T23:12:00.000-07:00</published><updated>2010-08-31T22:41:11.973-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>Launchorz</title><content type='html'>Release!
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/SHb6JgZQyRI/AAAAAAAAALc/Kuwew-aHsi8/s1600-h/lnz.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/SHb6JgZQyRI/AAAAAAAAALc/Kuwew-aHsi8/s320/lnz.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5221635859204327698" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;

For the past few weeks I have been working on a project for Windows automation. The result is Launchorz, Powerful JavaScript libraries for Win32 automation. 
&lt;br/&gt;&lt;br/&gt;
Look at this!
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Process.open('notepad.exe')
Mouse.move(1,1);
Time.sleep(500);
Keyboard.send('hello!')
Dialog.askYesNo('Question','Did you expect this to happen?');
&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;&lt;br/&gt;
LnzScript is the language of the Launchorz project. It is JavaScript (run locally, not in a web browser) with libraries for simulating keystrokes, mouse movements, working with files, and a lot more.
&lt;br/&gt;&lt;br/&gt;
Read the actually substantial documentation: &lt;br/&gt;&lt;a href="http://b3nf.com/codepages/lnz/"&gt;http://b3nf.com/codepages/lnz/&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Try it now! Download and get the source from Google code:&lt;br/&gt; 
&lt;a href="http://code.google.com/p/launchorz/downloads/list"&gt;http://code.google.com/p/launchorz/downloads/list&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Look at what it can do:&lt;br/&gt;
&lt;a href="http://b3nf.com/codepages/lnz/reference/lnz_04_documentation.xml"&gt;Reference&lt;/a&gt;

&lt;br/&gt;&lt;br/&gt;

One cool aspect of the project: documentation is written inline with the C++ code. Then, a Python script creates an xml file from this documentation.
This same xml file is used for reading by C++, C#, and even rendering to the browser! I wrote an xsl transform that renders the documentation, so that it can be &lt;a href="http://b3nf.com/codepages/lnz/reference/lnz_04_documentation.xml"&gt;seen&lt;/a&gt; in a browser. If you look at the source, you'll notice that it is xml.
&lt;br/&gt;&lt;br/&gt;

AutoIt exists and is good at what it does. However, its syntax and minimal data structures will not do for non-trivial programs. More of my rational is given in the documentation.
&lt;br/&gt;&lt;br/&gt;
Leave a comment, or send me an e-mail if you are interested in joining the project. Please try it and give feedback - I will consider feature requests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3357566084614800510?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3357566084614800510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3357566084614800510' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3357566084614800510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3357566084614800510'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/07/launchorz.html' title='Launchorz'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_sIga-_J94mI/SHb6JgZQyRI/AAAAAAAAALc/Kuwew-aHsi8/s72-c/lnz.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1929730607014794316</id><published>2008-07-06T15:27:00.000-07:00</published><updated>2010-08-31T22:46:51.822-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Blinkbeat</title><content type='html'>Here is another of my audio experiments, called Blinkbeat. It uses the Caps/Num/Scroll lock lights on your keyboard to blink along with the song you are playing!

Click on the picture to see a video (Youtube):
&lt;a target="new" href="http://www.youtube.com/watch?v=HmoPdazUH8M"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/SHFHxg0kD3I/AAAAAAAAALU/ciEzrQ6IPKY/s320/pic.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5220032359048679282" /&gt;&lt;/a&gt;

&lt;br&gt;&lt;br&gt;
The FFT is used to find the energy at different frequencies. What I display is kind of like a histogram of the frequencies.
&lt;br&gt;&lt;br&gt;
I have also added a beat detection option, which is not finished but seems to do a decent job. For beat detection, I divide the song into short segments, and use the FFT to find the low-frequency energy in each segment. So, I now have another signal in time, which is the low-frequency-energy over time. Finally, I take a second FFT of this new signal, which is finding the frequencies in this signal. I estimate the rhythm of the song to be the peak in these results, and return the result in BPM.

&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;a href="http://neednom.com/bfisher/blog/jmn/audioexperimentsblinkbeat_lights_bin.zip"&gt;Windows binary&lt;/a&gt;&lt;br&gt;&lt;br&gt;

Check out the source with:&lt;br&gt;
svn checkout http://yaalp.googlecode.com/svn/trunk/csaudio/ 
&lt;br&gt;&lt;br&gt;

Known issues: The normalization of lengths isn't perfect. If you try to play a long clip, the lights and the audio will eventually lose sync. Also, I draw the bars by changing the dimensions of several button controls :).

&lt;h3&gt;WaveAudio Developments&lt;/h3&gt;
I've also been working on the WaveAudio sound library in C#. It's pretty nice to be able to write:
&lt;br&gt;&lt;br&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Sine note = new Sine(440.0);
WaveAudio wave = note.CreateAudio(1.0); //1 second
wave.Amplify(1.5);
AudioPlayer pl = new AudioPlayer();
pl.play(wave); // and now you hear it!
&lt;/code&gt;&lt;/pre&gt;

I spent a lot of time yesterday writing some really sweet code for synthesizing audio. By the end of the day I found out that &lt;a href="http://neednom.com/bfisher/blog/jmn/csaudioexperiments_synth.mp3"&gt;what I had&lt;/a&gt; sounded a lot like the cheesy "choir" effect on old keyboards!! Still, this is pretty cool, and there are many more parameters to tweak. I'm owe thanks to Wikipedia and Wikibooks for giving me ideas.
&lt;br&gt;&lt;br&gt;
I added an algorithm for autocorrelation pitch detection, which works pretty well. It can tell what pitch I'm singing, and works on various instruments as well.
&lt;br&gt;&lt;br&gt;
As usual, source at 
http://yaalp.googlecode.com/svn/trunk/csaudio/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1929730607014794316?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1929730607014794316/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1929730607014794316' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1929730607014794316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1929730607014794316'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/07/blinkbeat.html' title='Blinkbeat'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/SHFHxg0kD3I/AAAAAAAAALU/ciEzrQ6IPKY/s72-c/pic.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6312538510841104672</id><published>2008-06-27T14:58:00.001-07:00</published><updated>2010-08-31T22:49:57.247-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Audio Experiments!</title><content type='html'>This &lt;a href="#blinkbeatexperimentsdload"&gt;program&lt;/a&gt; is a little interactive demo of 3 "audio experiments" I came up with. (An &lt;a href="http://neednom.com/bfisher/blog/jmn/csaudioexperiments.mp3"&gt;example&lt;/a&gt; of its sounds). My goal here was to create interesting sounds, experiment with FFTs, and test my digital audio code - but more on that later.
&lt;br&gt;&lt;br&gt;
&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/SGVij9OVnOI/AAAAAAAAAK8/ZRvD39WvlL0/s1600-h/ss1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/SGVij9OVnOI/AAAAAAAAAK8/ZRvD39WvlL0/s320/ss1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5216684113248230626" /&gt;&lt;/a&gt;
First, there is the "Vibrato Lab". A quick overview: Wav files store a sound as a long list of numbers, representing air pressure vs. time. If the computer plays through this list of samples at a faster rate, we hear a higher pitch. For example, playing every other sample results in something sounding an octave higher and twice as fast. If the samples are played back at a &lt;i&gt;varying&lt;/i&gt; rate, the pitch of the output varies, making a comical wavy sound. This is how I wrote a vibrato effect.
&lt;br&gt;&lt;br&gt;

When testing the vibrato effect, I found that it could sound interesting if the vibrato effect were applied twice - something like vibrato(vibrato(sound)). So, this is what the vibrato lab does. Use the sliders to change the width (how widely the pitch varies) and the frequency of oscillation. Use the checkboxes to enable another vibrato effect. Play with it!
&lt;br&gt;&lt;br&gt;

Next, the "CrFeedback". Have you ever played with video feedback, where when you tilt the camera the picture spirals and twists? When experimenting with audio code, I invented an effect that I like to think of this as the audio analog of those spirals. In this case, the song spirals out of control and degenerates into interesting noise. What you hear is like the sum of the song and many slower versions of itself. Play with the parameters - the "right" settings seem to be dependent on the clip you choose.
&lt;br&gt;&lt;br&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/SGVips5hwvI/AAAAAAAAALE/wLNwmBje254/s1600-h/ss2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/SGVips5hwvI/AAAAAAAAALE/wLNwmBje254/s320/ss2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5216684211945194226" /&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Finally, "Frequency Sketch". Last semester I took a course in Signals and Systems. Fourier transforms and the like are very cool, and the math was interesting. Most of the course dealt with signals in the context of voltage and current, but I wanted to apply what I had learned to audio. I wrote fourier transform code, and played around with discrete fourier transforms of audio significantly. As the name implies, the FFT is a faster algorithm for this, and so I replaced my fourier code with some FFT code by Don Cross, originally in C. Anyways, in this experiment, you can draw in the frequency domain, and hear the result! I've wanted something like this for a while, and there is still much left to study.
&lt;br&gt;&lt;br&gt;
Microsoft Paint is the user interface for Frequency Sketch :).  If you draw red pixels or lines on "input.png", you can hear the results. For example, drawing one red dot creates one sine wave, and so you will hear one pitch. The axes in blue show Hz. Note that the highest red pixel is chosen for a given frequency if there are more than one red pixels in a column. So far I've created ambient, flute-like, and eerie siren effects, but this is just the beginning.&lt;br&gt;&lt;br&gt;
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/SGVium6HxUI/AAAAAAAAALM/WZYz5JlrNDU/s1600-h/ss3.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/SGVium6HxUI/AAAAAAAAALM/WZYz5JlrNDU/s320/ss3.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5216684296236418370" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;

All of this was accomplished in a few days - using a library of digital audio code I've been working on for the past few weeks. I created a project in C# called CsWaveAudio; features include saving and opening wave files, synthesizing sounds, FFT, and certain effects. One interesting part of the library is that it stores samples as doubles, between -1.0 and 1.0. This makes it much easier to do calculations - and when writing effects you don't need to consider whether you are in 8-bit or 16-bit. The library also includes code for some "instruments" made from adding sine waves.
&lt;br&gt;&lt;br&gt;

&lt;h4&gt;&lt;a name="blinkbeatexperimentsdload"&gt;&lt;/a&gt;Downloads&lt;/h4&gt;&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/jmn/audioexperimentsblinkbeats_bin.zip"&gt;Windows binary&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/jmn/csaudioexperiments.mp3"&gt;Examples of sounds&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/jmn/CsWaveAudioSrc.zip"&gt;CsWaveAudio&lt;/a&gt;, a C# digital audio library, GPL&lt;br&gt;&lt;br&gt;

And, get all of the code:
svn checkout http://yaalp.googlecode.com/svn/trunk/csaudio/

&lt;br&gt;&lt;br&gt;
More notes: in the vibrato lab, the "tremolo" option creates oscillation in amplitude, not in pitch. Also, try clicking numbers to see what happens. The code for the CrFeedback effect is in the readme file - check out the svn source to modify it. There is a "randomize phases" checkbox in frequency sketch and I'm still not sure why this makes such a difference. Beat frequencies can be created because of the spacing between pixels in the input image. Maybe I should look into interpolation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6312538510841104672?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6312538510841104672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6312538510841104672' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6312538510841104672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6312538510841104672'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/06/audio-experiments.html' title='Audio Experiments!'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/SGVij9OVnOI/AAAAAAAAAK8/ZRvD39WvlL0/s72-c/ss1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2177382636661027876</id><published>2008-06-23T23:28:00.001-07:00</published><updated>2008-06-23T23:31:39.123-07:00</updated><title type='text'>LaTeXinWord</title><content type='html'>When I have time, I type up notes from school in LaTeX, so that they will be archived and legible for the future. This works ok, but I use many quick illustrations and diagrams, which are a pain to add to a LaTeX document (or at least are in any editor I've used). Also, even when the picture is saved, and in the right format, you can't really control positioning. By design, LaTeX has its own ideas about where it thinks your figure should go. This is good for actual "figures" as you would find in a paper, but I want my little diagrams to be inline with the text and not on the next page. In Word, by contrast, I can select an image and Ctrl C, Ctrl V it right into the document, but LaTeX's formatting of formulas is so much better.
&lt;br&gt;&lt;br&gt;

As it turns out, I can have a combination of the two. There is a good open source project, &lt;a href="http://latexinword.sourceforge.net/"&gt;LatexInWord&lt;/a&gt; which I recently discovered. Now I can keep the formulas I was using but have more control over layout of pictures. The interface is good - all one needs to do is press Alt-L to insert Latex. Also, I can have this open side-by-side with Lyx in view source mode, or another editor, and copy and paste. (There appear to be similar macros for OpenOffice, like &lt;a href="http://ooolatex.sourceforge.net/"&gt;this&lt;/a&gt;.)
&lt;br&gt;&lt;br&gt;

Of course, I'll still write my actual papers in standard LaTeX, but for my personal notes, this should work well. 
&lt;br&gt;&lt;br&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/SGCUI0T1DOI/AAAAAAAAAK0/Z9ZNNcjCVYw/s1600-h/ltx.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/SGCUI0T1DOI/AAAAAAAAAK0/Z9ZNNcjCVYw/s320/ltx.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5215331247696710882" /&gt;&lt;/a&gt;

Tonight I wrote a quick macro to be used with LatexInWord. It prints out the LaTeX source for all of the formulas in the document. I thought this would be useful to have for archival purposes (in the future I might not have LatexInWord or Word so a plain text copy is good). This is basically my first Word vba script, but it worked when I tested it.
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;Sub ExportLatexAsText()
    Dim strRes As String
    Dim i As Integer
    Dim newline As String
    newline = Chr(13) &amp;amp; Chr(10)
    i = 1
    For Each currentShape In ActiveDocument.InlineShapes
        If currentShape.AlternativeText &amp;lt;&amp;gt; &amp;quot;&amp;quot; Then
            strRes = strRes &amp;amp; newline &amp;amp; newline &amp;amp; &amp;quot;Formula &amp;quot; &amp;amp; i &amp;amp; newline
            strRes = strRes &amp;amp; currentShape.AlternativeText
            i = i + 1
        End If
    Next
    Selection.Text = strRes

End Sub
&lt;/code&gt;&lt;/pre&gt;&lt;br&gt;&lt;br&gt;
You can add this to the document under Macros. Just start editing an existing macro, and when you're looking at the source, put this subroutine in.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2177382636661027876?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2177382636661027876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2177382636661027876' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2177382636661027876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2177382636661027876'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/06/latexinword.html' title='LaTeXinWord'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_sIga-_J94mI/SGCUI0T1DOI/AAAAAAAAAK0/Z9ZNNcjCVYw/s72-c/ltx.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5432075628477230552</id><published>2008-06-20T08:15:00.000-07:00</published><updated>2010-08-31T22:52:00.012-07:00</updated><title type='text'>Googlecalc</title><content type='html'>This has been done before several times, but usually in Perl. After reading a version by Matt Sparks, I quickly wrote a C# version. I think this is something that will be useful. You type in an expression, which is sent to Google, 
and the result is printed back.

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_sIga-_J94mI/SFvJrBJYQ7I/AAAAAAAAAKs/NHduC0YPRQY/s1600-h/ex.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/SFvJrBJYQ7I/AAAAAAAAAKs/NHduC0YPRQY/s320/ex.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5213982734490813362" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/jmn/bin.zip"&gt;Bin&lt;/a&gt; (Windows exe) 
&lt;a href="http://neednom.com/bfisher/blog/jmn/gcalcsrc.zip"&gt;Source&lt;/a&gt;, project file is Vs2008 but the source should work in 2005.
&lt;br&gt;&lt;br&gt;
On another note, while I have enjoyed reading entries in the 
&lt;a href="http://en.wikipedia.org/wiki/IOCCC"&gt;Obfuscated C Code Contest&lt;/a&gt; for a while, there is another interesting contest that sounds just as fun.
The &lt;a href="http://en.wikipedia.org/wiki/Underhanded_C_Contest"&gt;Underhanded C Contest&lt;/a&gt; is a "programming contest to turn out code that is malicious, but passes a rigorous inspection, and looks like an honest mistake."
&lt;br&gt;&lt;br&gt;
Another clever contest is the &lt;a href="http://graphics.stanford.edu/~danielrh/vote/vote.html"&gt;Obfuscated V&lt;/a&gt; contest in 2004, where contestants wrote short programs that appear to simply count votes, but actually influence the results.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5432075628477230552?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5432075628477230552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5432075628477230552' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5432075628477230552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5432075628477230552'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/06/googlecalc.html' title='Googlecalc'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/SFvJrBJYQ7I/AAAAAAAAAKs/NHduC0YPRQY/s72-c/ex.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-396977343958587960</id><published>2008-06-19T08:02:00.000-07:00</published><updated>2008-06-19T08:04:53.447-07:00</updated><title type='text'>Summer</title><content type='html'>I have been writing code, but that will have to wait. I just needed to post something since it's been a while.
&lt;br&gt;&lt;br&gt;

In Java, Math.abs can return a negative number (!). I heard about it from &lt;a href="http://bmaurer.blogspot.com/2006/10/mathabs-returns-negative-number.html"&gt;here&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;

Python 2.6 is in beta. In Python, last fall Guido &lt;a href="http://www.artima.com/weblogs/viewpost.jsp?thread=214235"&gt;talked&lt;/a&gt; about the GIL. Now I am interested to see a "multiprocessing" module which "supports the spawning of processes using a similar API of the threading module". This is pretty cool - it will allow programs to take advantage of multiple processors, while maintaining the GIL.
&lt;br&gt;&lt;br&gt;

A Python 3k hack from &lt;a href="http://mail.python.org/pipermail/python-3000/2006-May/002033.html"&gt;here&lt;/a&gt;.
At least on the Python 3k I'm using , __signature__ isn't available yet, so this doesn't work yet. Annotations can be useful though.&lt;br&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;#posted by Nick Coghlan
#on Python-3000 list,
#http://mail.python.org/pipermail/python-3000/2006-May/002033.html

#Apparently __signature__ hasn't been added yet. This won't work yet.

class Annotate(object):
     def __init__(*args, **kwds):
         self, args = args[0], args[1:]
         self.arg_notes = args
         self.kwd_notes = kwds
         self.return_note = None
         self.strict = False

     @classmethod
     def strict(*args, **kwds):
         cls, args = args[0], args[1:]
         self = cls(*args, **kwds)
         self.strict = True
         return self

     def returns(self, note):
         self.return_note = note
         return self

     def __call__(self, func):
         func.__signature__.update_annotations(self)
         return func

@Annotate(str, str, int).returns(int)
def f(a, b, c=0):
     # some operation producing an int. . .

@Annotate.strict(str, str, int).returns(int)
def f(a, b, c=0):
     # some operation producing an int. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;&lt;br&gt;
I am currently working for &lt;a href="http://www.avid.com"&gt;Avid&lt;/a&gt; in Tewksbury, MA, as a "Media Engine Infrastructure Software Engineer Intern". It's going well so far as I dig deeper and deeper into c++.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-396977343958587960?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/396977343958587960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=396977343958587960' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/396977343958587960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/396977343958587960'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/06/summer.html' title='Summer'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6800114206758516043</id><published>2008-04-28T12:51:00.001-07:00</published><updated>2010-08-31T21:39:49.802-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Consonance</title><content type='html'>When two notes of close frequency are played simultaneously, one can hear what is known as a beat frequency. Say two violinists are playing. One plays an A, 440Hz and the other is playing slightly sharp at 445Hz. One hears a 5Hz "beat" buzzing, that arises from the sum of the two sine waves.
&lt;br&gt;
&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/SBYrnUeYxcI/AAAAAAAAAKk/1UdBzszEcFQ/s1600-h/sinebeats.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/SBYrnUeYxcI/AAAAAAAAAKk/1UdBzszEcFQ/s320/sinebeats.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5194387174729500098" /&gt;&lt;/a&gt;
&lt;br&gt;
When the two tones are very close, the sound is no longer unpleasant, and the result sounds like a "smooth", flowing note. When the tones are less close, one hears the "buzzing", vibrating beats. When the two tones are significantly separated, one simply hears two notes. In the 19th century, the physicist Helmholtz posed a theory: what if humans dislike the sound of beats, and that the more the beats are noticed, the more "rough" the sound is perceived. He developed a "roughness" curve based on some experiments:&lt;br&gt;
&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/SBYrN0eYxbI/AAAAAAAAAKc/VZuN0ocqzwo/s1600-h/beats.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/SBYrN0eYxbI/AAAAAAAAAKc/VZuN0ocqzwo/s320/beats.PNG" border="0" alt="(lambda*x^2)/((33^2+x^2)^2)" title="(lambda*x^2)/((33^2+x^2)^2)" id="BLOGGER_PHOTO_ID_5194386736642835890" /&gt;&lt;/a&gt;

&lt;br&gt;&lt;br&gt;


Now, if we go back to the example of the two violins, we can predict how pleasant the two sounds will be to each other. This gets interesting, because a violin plays more than the fundamental note; harmonics are played also. So if the first violin plays an A, and the other plays a slightly offtune E, there will be roughness between the 3rd harmonic of the A (E'), and the 2nd harmonic of the E (E'). If the roughness for each interaction for 6 harmonics is summed, an interesting pattern emerges:

&lt;br&gt;&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/SBYq7EeYxaI/AAAAAAAAAKU/I4acXmVeIqM/s1600-h/helm.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/SBYq7EeYxaI/AAAAAAAAAKU/I4acXmVeIqM/s320/helm.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5194386414520288674" /&gt;&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;

This figure plots the sum of roughness for 6 harmonics, when one plays a fixed note, and the other violin changes pitch. The peaks are areas of high roughness, and the valleys are lower roughness. The notches in this plot line up perfectly with the intervals we use in music! The central valley is the interval of a perfect fifth. The other dips are other familiar intervals. This offers an explanation of why certain intervals are more "consonant" than others, and more. In developing temperament systems, it was found that some approximations of notes are more tolarable than others. For example, we are very perceptive to a slightly-off 5th, but not to an adjusted 3rd. This might correspond graphically with the "steeper" valley of the 5th. Also, the shallower valleys reasonably match with what we consider less consonant intervals.
&lt;br&gt;&lt;br&gt;
Using this, Helmholtz was able to explain quantitatively why a major 3rd played by a clarinet and oboe sounds better when the clarinet plays the lower tone (the clarinet sounds only odd harmonics).
&lt;br&gt;&lt;br&gt;
The figure is a screenshot from a program I wrote to explore this. When you click on the plot, you hear the interval. It also includes a modern model of roughness,  Vassilakis (2007).&lt;br&gt;&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/09audio/audiopad/audiointerval.zip"&gt;Download&lt;/a&gt; (Windows, .NET 2.0)
&lt;a href="http://neednom.com/bfisher/blog/09audio/audiopad/audiointerval_source.zip"&gt;Source&lt;/a&gt; (including Matlab plots)
&lt;br&gt;&lt;br&gt;&lt;br&gt;But do beats and perceived roughness cause dissonance, or are they just correlated with dissonance? Helmholtz's theory does not explain everything. Even when beats are eliminated, the intervals sound dissonant. The notes of our scale are also found in the harmonic series. One could alternatively say that notes are consonant when their harmonics match. &lt;a href="http://www.jstor.org/sici?sici=0021-8529(196221)20%3A3%3C301%3ASTOMC%3E2.0.CO%3B2-V"&gt;
This paper&lt;/a&gt; notes many criticisms of Helmholtz and the theory of roughness. I will have to study this further.
&lt;br&gt;&lt;br&gt;
References:
"On the &lt;a href="http://books.google.com/books?id=GwE6AAAAIAAJ&amp;printsec=frontcover&amp;dq=helmholtz+on+the+sensations+of+tone
"&gt;Sensations of Tone&lt;/a&gt; as a Physiological basis for the Theory of Music" by Hermann Helmholtz, translated by Ellis
&lt;br&gt;
&lt;a href="http://www.acousticslab.org/learnmoresra/moremodel.html"&gt;Roughness Calculation Model&lt;/a&gt; by Vassilakis, 2007.
&lt;br&gt;

&lt;a href="http://www.jstor.org/sici?sici=0021-8529(196221)20%3A3%3C301%3ASTOMC%3E2.0.CO%3B2-V"&gt;Sensory Theories of Musical Consonance&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6800114206758516043?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6800114206758516043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6800114206758516043' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6800114206758516043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6800114206758516043'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/04/consonance.html' title='Consonance'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/SBYrnUeYxcI/AAAAAAAAAKk/1UdBzszEcFQ/s72-c/sinebeats.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1678885001778527965</id><published>2008-04-26T22:48:00.001-07:00</published><updated>2010-08-31T21:41:03.698-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Fun with Real-time Audio</title><content type='html'>Over the past week, I've had fun with real-time audio. I'd worked a bit with sound in the past, but in Python, where synthesizing audio is noticeably slow. This time, I coded in C#, which is a lot faster and has access to DirectX. I use DirectSound to put raw bytes of data right into a buffer, enabling seamless audio.
&lt;br&gt;&lt;br&gt;
After experimentation, I came up with a program that plays a continous tone. While it is playing, you can change the pulse-width and "smoothness", creating interesting sounds. Quickly changing pulse-width creates the illusion of motion. The "smoothness" parameter goes from sine wave-like to a square wave (approximated by quadratics). As another experiment, I added a joystick interface - if you plug in a game controller and open the program, you can use the joystick to control these parameters. I encourage the reader to think of more audio effects and interfaces.
&lt;br&gt;&lt;br&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/SBQT6EeYxZI/AAAAAAAAAKM/CDQC8MkTdEY/s1600-h/shotother.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/SBQT6EeYxZI/AAAAAAAAAKM/CDQC8MkTdEY/s400/shotother.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5193798158619559314" /&gt;&lt;/a&gt;
&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/SBQT20eYxYI/AAAAAAAAAKE/aWinGnP3L1M/s1600-h/shotjoy.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/SBQT20eYxYI/AAAAAAAAAKE/aWinGnP3L1M/s400/shotjoy.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5193798102784984450" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
I wrote a custom control plotting the waveform, which was useful. The program also contains my implementations of waves such as square, triangle, sawtooth, white noise, red noise.&lt;br&gt;&lt;br&gt;
As an aside, delegates in C# are very convenient, allowing you to easily store and pass refrences to methods. What is great is that there is so little code involved. Here's an example from my sound class:
&lt;br&gt;&lt;br&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;// Takes integer index, returns double between -1 and 1
delegate double SoundDelegate(int i);
...
double timeScale = frequency * 2 * Math.PI / (double) 44100;
SoundDelegate fn = delegate(int i)
{ 
    return Math.Sin(i * timeScale);
};
return create16bit_sound(fn);
&lt;/code&gt;&lt;/pre&gt;
Notice how the anonymous function can even keep references to variables. It's better than a Python lambda.
&lt;br&gt;&lt;br&gt;
Here is a &lt;a href="http://neednom.com/bfisher/blog/09audio/audiopad/sample.mp3"&gt;sample&lt;/a&gt;. The real thing sounds even better, the rate of updating was limited in the process of recording this sample.
&lt;br&gt;&lt;br&gt;
I found that smooth pitch-changing for real-time audio could be complicated. If you just change pitch, you hear a lot of "clicks" - the period doesn't line up with the size of the buffer, causing a click every refresh. I sidestepped this problem by only playing frequencies that fit into the buffer, but that's not a very good solution. If I took the previous phase into account, I could fix this.
&lt;br&gt;&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/09audio/audiopad/audiodx.zip"&gt;Download&lt;/a&gt; (Windows, .NET 2.0, DirectX9) Joystick is optional&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/09audio/audiopad/audiodx_source.zip"&gt;Source &lt;/a&gt; GPLv3
&lt;br&gt;&lt;br&gt;
If you want to write DirectC audio in C#,the following may help: To get the Managed DirectX dlls, I downloaded the "DirectX 9.0c Redistributable for Software Developers", ran it, and opened the resulting ManagedDx.cab file. Also, using these in Visual Studio 2005, I would get an exception upon starting the program. One can disable this false excption in  Debug-&gt;Exceptions-&gt;Managed Debugging Assistants, uncheck LoaderLock.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1678885001778527965?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1678885001778527965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1678885001778527965' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1678885001778527965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1678885001778527965'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/04/fun-with-real-time-audio.html' title='Fun with Real-time Audio'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_sIga-_J94mI/SBQT6EeYxZI/AAAAAAAAAKM/CDQC8MkTdEY/s72-c/shotother.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7527906209073801098</id><published>2008-04-15T14:32:00.000-07:00</published><updated>2008-04-15T14:38:57.311-07:00</updated><title type='text'>Maurader's Map Update</title><content type='html'>&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/SAUfRNq4pXI/AAAAAAAAAJ8/cqrRzpEErj0/s1600-h/a+lot+map-cropped.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/SAUfRNq4pXI/AAAAAAAAAJ8/cqrRzpEErj0/s400/a+lot+map-cropped.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5189588526202135922" /&gt;&lt;/a&gt;

Over 1/4th of the students at Olin are on the Maurader's Map!
&lt;br&gt;&lt;br&gt;
We improved the location algorithm. It is successfully at room-level precision, which is really cool, and the accuracy is very good. As you can see, we've added the ability to have custom icons. The client runs on Windows, Linux, and Mac now. 
&lt;br&gt;&lt;br&gt;
And, you can actually use it to find someone. Now we just have to persuade the professors to use it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7527906209073801098?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7527906209073801098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7527906209073801098' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7527906209073801098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7527906209073801098'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/04/mauraders-map-update.html' title='Maurader&apos;s Map Update'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/SAUfRNq4pXI/AAAAAAAAAJ8/cqrRzpEErj0/s72-c/a+lot+map-cropped.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2987178135198799520</id><published>2008-03-30T21:01:00.001-07:00</published><updated>2010-08-31T21:42:21.315-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>gedit Regular Expression Plugin</title><content type='html'>&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/R_Bk_iyBlcI/AAAAAAAAAJ0/OmlK3Dsg3EE/s1600-h/Screenshot2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/R_Bk_iyBlcI/AAAAAAAAAJ0/OmlK3Dsg3EE/s320/Screenshot2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5183754213934601666" /&gt;&lt;/a&gt;
&lt;br&gt;
A friend told me that gedit was missing a regular expressions plugin that could replace. I use gedit occasionally, and so I worked on this plugin. It uses Python's good regular expression module, and supports backreferences, making it possible to do significant text processing.
&lt;br&gt;&lt;br&gt;
This plugin is based on &lt;a href="http://rafaelsilva.net/node/103"&gt;extensão para o gEdit&lt;/a&gt; by Daniel Carvalho. I fixed bugs, cleaned up the interface, moved the menuitem to the Search menu, added backreferences support, added replace all, and added an option for case-sensitivity. Also, the search mode is multiline, so ^ matches the start of a line. 
&lt;a href="http://neednom.com/bfisher/blog/geditplugin/regex_replace.tar.gz"&gt;Download&lt;/a&gt;
&lt;br&gt;
To install, place the files in ~/.gnome2/gedit/plugins/. Then open gedit, choose Preferences from the Edit menu. On the plugins tab, you should see Regular Expression Replace in the list. Check to enable. Now, you should have a "Regular Expression" item in the Search menu. Tell me if you have any feedback.
&lt;br&gt;&lt;br&gt;
Backreferences are very useful. In the screenshot above, I've written a regular expression for turning "one.tim" into "tim.one", and so on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2987178135198799520?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2987178135198799520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2987178135198799520' title='60 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2987178135198799520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2987178135198799520'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/03/gedit-regular-expression-plugin.html' title='gedit Regular Expression Plugin'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/R_Bk_iyBlcI/AAAAAAAAAJ0/OmlK3Dsg3EE/s72-c/Screenshot2.PNG' height='72' width='72'/><thr:total>60</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7174090762318231664</id><published>2008-03-22T20:25:00.000-07:00</published><updated>2010-08-31T21:45:10.058-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Collect'/><title type='text'>RenameCells</title><content type='html'>Renaming files in Windows Explorer is somewhat slow - select, F2, type a new name, select the next file, and repeat. I thought it would be cool to instead use a spreadsheet interface - where pressing Enter drops you down to the next line.
&lt;br&gt;&lt;br&gt;
So, I wrote RenameCells, a Python program for quick renaming. I also added features for batch renaming like Rename by Pattern, Replace, and Regular Expressions. You can see all of the changes in a grid before you press Rename. The grid interface also lets you copy and paste from Excel or another program. See the &lt;a href="http://neednom.com/bfisher/blog/renamecells/manual.html"&gt;tutorial&lt;/a&gt; for information about all of this.

&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/R-XOWyyBlbI/AAAAAAAAAJs/SSaVw-mlSjg/s1600-h/02.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/R-XOWyyBlbI/AAAAAAAAAJs/SSaVw-mlSjg/s320/02.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5180773837343659442" /&gt;&lt;/a&gt;
&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/renamecells/manual.html"&gt;Tutorial&lt;/a&gt; explaining use.&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/renamecells/renamecells_win.zip"&gt;Download&lt;/a&gt; for Windows (just unzip it and run RenameCells.exe)&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/renamecells/source.zip"&gt;Python source&lt;/a&gt;, cross-platform, requires wxPython.
&lt;br&gt;&lt;br&gt;
The source should be cross-platform but I have not tested thoroughly except on Windows. If you change your mind you can hit Undo, and if something goes wrong, choosing "Repair" from the "Rename" menu will clean up temporary files.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7174090762318231664?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7174090762318231664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7174090762318231664' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7174090762318231664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7174090762318231664'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/03/renamecells.html' title='RenameCells'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/R-XOWyyBlbI/AAAAAAAAAJs/SSaVw-mlSjg/s72-c/02.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3035035519247670577</id><published>2008-03-08T13:41:00.000-08:00</published><updated>2010-08-31T21:45:44.834-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='See'/><title type='text'>Pythonpixels</title><content type='html'>A few days ago, I wrote Pythonpixels, an original interface for image processing. (&lt;a href="#pixdownload"&gt;Download&lt;/a&gt;).&lt;br&gt;&lt;br&gt;
Let's say you want to quickly prototype a new imaging algorithm. As a simple example, you are increasing the Red channel of an image by 40. Typically, this would first involve quite a bit of code, in order to gain pixel-level access to the image. Also, once testing your project, you would need to set up a test framework and enter the debug/recompile cycle.&lt;br&gt;&lt;br&gt;
With Pythonpixels, this whole process is as easy as typing
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;map:
    R=r+40
&lt;/code&gt;&lt;/pre&gt;
Lower-case "r" means the old red value, and "R" means the new red value. That's all there is to it. To test the effect, type this into a box, press Run, and you see the results instantly.&lt;br&gt;&lt;br&gt;
If you wanted to create a gradient,
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;loop:
    R=x+y
    G=x-y
    B=0
&lt;/code&gt;&lt;/pre&gt;
(It's fun to experiment making patterns this way. In the examples that come with Pythonpixels, I played around with sin and cos as well to make funky patterns.)&lt;br&gt;&lt;br&gt;
Also, what you type is interpretted as Python code. (Any value &gt; 255 or &lt; 0 is truncated for you). So, you can include complicated logic, and read the output from print statements.  &lt;br&gt;&lt;br&gt;
If you want to do something more advanced, the entire image is exposed as imgInput. The following example makes your image all wavy.
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;from math import sin, cos, atan2, sqrt, pi
midx = width/2
midy = height/2
nwave = 12
imgOutput = ImageChops.duplicate(imgInput)
imgInputArray = imgInput.load()
imgOutputArray = imgOutput.load()
twopiconst = 2.0 * pi / 128.0
loop:
    newx = x + nwave*sin( y * twopiconst)
    newy = y + nwave*cos( x * twopiconst)
    if (newy&amp;gt;0 and newy&amp;lt;height and newx&amp;gt;0 and newx&amp;lt;width):
        imgOutputArray[x,y] = imgInputArray[newx,newy]
&lt;/code&gt;&lt;/pre&gt;&lt;br&gt;&lt;br&gt;
Or hue shift:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;loop_hsl:
    H=h+0.2
&lt;/code&gt;&lt;/pre&gt;&lt;br&gt;&lt;br&gt;
It comes with other effects, including convolution matrices, fractals, kaleidoscope, and posterize. Data-oriented operations work very well, too, like finding pixel values, statistics, or making a histogram. Other features include pasting an image from the clipboard and batch-processing a folder of images.&lt;br&gt;&lt;br&gt;
&lt;a name="pixdownload"&gt;&lt;/a&gt;Try it out!  &lt;a href="http://neednom.com/bfisher/blog/pixels/pythonpixels0.1.zip"&gt;Windows Download&lt;/a&gt; (3.7Mb, GPL). Unzip the file and run Pythonpixels.exe. Let me know what you think, and show me scripts you come up with.
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/pixels/pythonpixels0.1src.zip"&gt;Source&lt;/a&gt; Python 2.5 (GPL). Requires packages 
tkinter, 
python-imaging (PIL), 
python-imaging-tkinter (ImageTK).
&lt;br/&gt;&lt;br/&gt;
You can check out the source at http://code.google.com/p/pythonpixels/.&lt;br&gt;&lt;br&gt;
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/R9MIUjWpWAI/AAAAAAAAAJk/HcT6HDccwC4/s1600-h/ppixels.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/R9MIUjWpWAI/AAAAAAAAAJk/HcT6HDccwC4/s320/ppixels.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5175489545959462914" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
&lt;b&gt;More details:&lt;/b&gt;
The keyword "loop" loops through every pixel, (providing the variables x and y), and if you assign to R, G, or B, the output value at that position will be changed.&lt;br&gt;&lt;br&gt;
The keyword "map" sets up a precomputed table for red, green, and blue values. This makes the effect very fast, but limits what can be done - the effect cannot depend on x,y position, and hsv or hsl cannot be used.&lt;br&gt;&lt;br&gt;
"loop_hsv" and "loop_hsl" work in the same way as loop but are in different color spaces.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3035035519247670577?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3035035519247670577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3035035519247670577' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3035035519247670577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3035035519247670577'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/03/pythonpixels.html' title='Pythonpixels'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_sIga-_J94mI/R9MIUjWpWAI/AAAAAAAAAJk/HcT6HDccwC4/s72-c/ppixels.PNG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3889918277133546244</id><published>2008-03-04T13:06:00.000-08:00</published><updated>2008-03-08T14:39:52.714-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>Maurader's Map - In Use</title><content type='html'>&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/R826KVifpXI/AAAAAAAAAJc/Z_Vi6fbzBFQ/s1600-h/mmap.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/R826KVifpXI/AAAAAAAAAJc/Z_Vi6fbzBFQ/s320/mmap.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5173996233661850994" /&gt;&lt;/a&gt;
We've released a beta of the map, and people are starting to use it. (That's a real screenshot of the people online right now.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3889918277133546244?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3889918277133546244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3889918277133546244' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3889918277133546244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3889918277133546244'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/03/mauraders-map-in-use.html' title='Maurader&apos;s Map - In Use'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/R826KVifpXI/AAAAAAAAAJc/Z_Vi6fbzBFQ/s72-c/mmap.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-8537511450845547172</id><published>2008-02-23T17:41:00.000-08:00</published><updated>2010-08-31T21:46:37.783-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>Unplot</title><content type='html'>Sometimes I want to "un-plot" data. I find myself in the situation where I have a plot of the data, but not the actual coordinates of the data itself. So, today I wrote this little tool for turning a bitmap image into the x and y coordinates of the curve. (Some Photoshop work may be necessary for cleaning out other lines in the image). It simply takes the image, starts at the top, and scans downwards until it hits the curve, and then works from left to right. The data can be saved as CSV, to be easily loaded into Excel or Matlab.

&lt;br&gt;&lt;br&gt;
&lt;a href="http://neednom.com/bfisher/blog/unplot/swout.htm"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/R8DMh1hItcI/AAAAAAAAAJU/OsSMD4PpqQs/s320/example_audacity.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5170357253895140802" /&gt;&lt;/a&gt;&lt;br&gt;
I've tested this on figures from research papers, and it works. This will be useful for recovering data from a figure when the original data has been lost. Also, I can use it to get audio data from Audacity.
&lt;br&gt;&lt;br&gt;
Watch this &lt;a href="http://neednom.com/bfisher/blog/unplot/swout.htm"&gt;screencast&lt;/a&gt; of it in action. This will show how to use it.
&lt;br&gt;
&lt;br&gt;
And here is the &lt;a href="http://neednom.com/bfisher/blog/unplot/unplot.zip"&gt;program&lt;/a&gt; (897K). Currently Windows only, but I can port it to Linux if there is interest.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-8537511450845547172?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/8537511450845547172/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=8537511450845547172' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8537511450845547172'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8537511450845547172'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/02/unplot.html' title='Unplot'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/R8DMh1hItcI/AAAAAAAAAJU/OsSMD4PpqQs/s72-c/example_audacity.PNG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4220643526600183028</id><published>2008-02-23T17:19:00.001-08:00</published><updated>2010-08-31T22:06:06.935-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Speak'/><title type='text'>WordMash</title><content type='html'>&lt;a href="http://neednom.com/bfisher/blog/wordmash/mash01.php"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/R8DGSVhItbI/AAAAAAAAAJM/ksXY3t60nss/s320/logo.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5170350390537401778" /&gt;&lt;/a&gt;



In high school, I wrote a short JavaScript program to create &lt;a href="http://en.wikipedia.org/wiki/Markov_chain"&gt;Markov chains&lt;/a&gt; while waiting in an airport. The idea is simple but yields incredible results. First, you enter some lengthy text. A model is created by keeping track of all of the words that follow a given word. If there is a fairly large input text (Sherlock Holmes, or the Times), the slight amount of context will be enough to create sentences that (almost) make sense:
&lt;br&gt;&lt;br&gt;
&lt;blockquote&gt;I told you yesterday, and I trust, with you, however, as I spoke, and it is clear and concise.&lt;/blockquote&gt;
&lt;br&gt;&lt;br&gt;
However, the randomness produces results that are often ridiculous!

&lt;br&gt;&lt;br&gt;
&lt;blockquote&gt;Mr. Gordon, a retired Marine lieutenant general and a metal-schooled three-ax attack into songs that are both action-packed and gratuitously stylized. "Where Can I Stab Myself in the course of research for gut-level emotional impact," sheriff's officers said.&lt;/blockquote&gt;
&lt;br&gt;&lt;br&gt;
I recently made a &lt;a href="http://neednom.com/bfisher/blog/wordmash/mash01.php"&gt;site&lt;/a&gt; called WordMash where you can try it yourself, with texts like The Adventures of Sherlock Holmes. Best of all, you can paste your own text in, and see what happens. The code is all GPL, so have fun with it. Leave a comment if you come up with something good.
&lt;br&gt;&lt;br&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4220643526600183028?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4220643526600183028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4220643526600183028' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4220643526600183028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4220643526600183028'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/02/wordmash.html' title='WordMash'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/R8DGSVhItbI/AAAAAAAAAJM/ksXY3t60nss/s72-c/logo.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4980390147757069131</id><published>2008-02-12T12:46:00.001-08:00</published><updated>2010-08-31T21:47:32.161-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='See'/><title type='text'>3 Windows Utilities</title><content type='html'>First, a rich text-to HTML converter. You can use it alongside WordPad to get a ghetto Dreamweaver.
&lt;br/&gt;&lt;br/&gt;
I was looking for a quick way to convert some RTF documents to HTML. Parsing the RTF was too much effort. Pasting into Dreamweaver seemed to strip formatting. I then thought of an idea that would make this very easy.
You can paste rich text into a "ContentEditable" element in Internet Explorer, and it will retain much of its formatting - and then getting the innerHTML of that element produces the HTML! 
&lt;br/&gt;&lt;br/&gt;
Bold/italic/underline, fonts, colors, indentation, and even tables(!) are supported. I threw together a demo &lt;a href="http://neednom.com/bfisher/blog/3win/rtf2html/rtf_html.html"&gt;here&lt;/a&gt;. (Requires Internet Explorer). Also of note is &lt;a href="http://www.kevinroth.com/rte/demo.htm"&gt;this&lt;/a&gt; cross-browser rich text editor that also works in Mozilla using &lt;a href="http://kb.mozillazine.org/Firefox_:_Midas"&gt;designMode&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
Next, a small tool for finding colors. You know the eyedropper tool in Photoshop that can pick up a color from an image? This will do something similar for anything visible on your screen. It will grab the RGB value of the color just underneath your cursor, in any Windows program.
&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/R7IF0VhItZI/AAAAAAAAAI8/aUJuc62_ciI/s1600-h/peekcolor.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/R7IF0VhItZI/AAAAAAAAAI8/aUJuc62_ciI/s320/peekcolor.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5166198119234844050" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Download it &lt;a href="http://neednom.com/bfisher/blog/3win/peekColor.zip"&gt;here&lt;/a&gt; (zip, no installer). Run the program and press the space bar to update the color to be whatever color is beneath the mouse. The idea came from altAnswerColor from Altuit.
&lt;br/&gt;&lt;br/&gt;
Finally, I use Microsoft Paint occasionally to draw simple lines and diagrams. To get arrows, I'll have to copy screenshots from Word or something. I wrote this &lt;a href="http://neednom.com/bfisher/blog/3win/arrows.zip"&gt;small program&lt;/a&gt; to have quicker access to arrows.&lt;br/&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/R7IHRlhItaI/AAAAAAAAAJE/vUW7VzaZJWo/s1600-h/arrows_s.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/R7IHRlhItaI/AAAAAAAAAJE/vUW7VzaZJWo/s320/arrows_s.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5166199721257645474" /&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
When you click an arrow, it places an image on the clipboard that can be quickly pasted into Paint. Simple, yet useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4980390147757069131?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4980390147757069131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4980390147757069131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4980390147757069131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4980390147757069131'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2008/02/3-windows-utilities.html' title='3 Windows Utilities'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/R7IF0VhItZI/AAAAAAAAAI8/aUJuc62_ciI/s72-c/peekcolor.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1039867286957282723</id><published>2007-12-23T00:48:00.001-08:00</published><updated>2008-03-08T14:39:52.715-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>The Marauder's Map of Olin</title><content type='html'>A friend and I have been planning this for a few months. On a laptop, we can find the signal strengths of wireless access points, and use this information to determine location. A few days ago, I sat down and coded it, in time for Expo. My poster probably says it best:
&lt;br/&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/R24hLWSle0I/AAAAAAAAAI0/ZvCgSeGSlQg/s1600-h/mmolin.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/R24hLWSle0I/AAAAAAAAAI0/ZvCgSeGSlQg/s320/mmolin.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5147087902976867138" /&gt;&lt;/a&gt;
&lt;br/&gt;
Instead of using triangulation, it simply acquires a lot of data. (Triangulation did not work as well, because of the many walls and windows in our dorm.) The database simply records associations of a set of wireless signals with a name ("AC Room 318") and a point on a map. To look up a position, the server looks for a similar set of values with a distance formula, and returns the closest match. When the program is run, it recieves feedback from users, and every time adds a new association. Over time, the results get better and better. From successful tests, I think room-level precision may be possible.
&lt;br/&gt;&lt;br/&gt;
We were able to figure out the right WMI query to find signal strengths other than the current connection (in Windows). I then wrote a small C# app that runs the query and prints the results to stdout. 
&lt;br/&gt;&lt;br/&gt;
A Python program gathers this data, taking the average of three runs, and sends it to the server.  A PHP script forms a vector, thinking of each of the 76 access points as an axis, and looks for nearby vectors in the database.   The location of the user, and status, is also stored in a centralized database.
&lt;br/&gt;&lt;br/&gt;
Finally, in a web app you can view locations of everyone who is logged in. Hovering over the icons tells you who and where the person is. (Only if you swear that if you are up to no good).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1039867286957282723?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1039867286957282723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1039867286957282723' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1039867286957282723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1039867286957282723'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/12/marauders-map-of-olin.html' title='The Marauder&apos;s Map of Olin'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_sIga-_J94mI/R24hLWSle0I/AAAAAAAAAI0/ZvCgSeGSlQg/s72-c/mmolin.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3928829274262739336</id><published>2007-12-10T23:39:00.000-08:00</published><updated>2010-08-31T21:48:06.326-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>Improbability</title><content type='html'>I'm in ProbStat this semester. I didn't feel like working so I wrote a quick little program.&lt;br/&gt;&lt;br/&gt;
Tools like &lt;a href="http://www.anu.edu.au/nceph/surfstat/surfstat-home/tables/normal.php"&gt;SurfStat&lt;/a&gt; are nice, but they aren't quite what I want.
So, I wrote a similar web app, &lt;a href="http://neednom.com/bfisher/blog/improbability/"&gt;Improbability&lt;/a&gt;. You can tab through the fields more easily and simply press Return to evaluate. Also, there is a graphical display of the area under the curve (using CSS and transparency).
&lt;br/&gt;&lt;br/&gt;
So far, just the normal distribution until I'm satisfied with the interface, but other distributions will be pretty easy to add. I just wrote this, and am open to suggestions as to making it better.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3928829274262739336?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3928829274262739336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3928829274262739336' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3928829274262739336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3928829274262739336'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/12/improbability.html' title='Improbability'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5017387885104777241</id><published>2007-12-07T23:57:00.000-08:00</published><updated>2010-08-31T21:48:26.202-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>Math Console</title><content type='html'>Here's my old &lt;a href="http://neednom.com/bfisher/blog/mathconsole/console.html"&gt;Math Console&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
Before I had ever used Matlab, I wrote something resembling it (in JavaScript, of all things). I used it at the time for my statistics class, and kept the code kicking around, but never got around to finishing everything that I had planned. (I had wanted to add many more statistics features). One of the factors was JavaScript's lack of support for overloading operators, which kind of stopped things. Also, there isn't really a convenient way to support complex numbers.
&lt;br&gt;&lt;br&gt;
Today I made it (somewhat) cross-platform. Since this might be as complete as it will get, I'll call this the last version. Open the page and you can click a link to see a list of features and provided functions.
&lt;br&gt;&lt;br&gt;
I still think this is a pretty cool project, and I am just now working on something similar in Python.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5017387885104777241?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5017387885104777241/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5017387885104777241' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5017387885104777241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5017387885104777241'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/12/math-console.html' title='Math Console'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-9109247557197089069</id><published>2007-11-13T17:00:00.000-08:00</published><updated>2010-08-31T21:48:54.724-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>Tetris Overflow - On Camera</title><content type='html'>As explained in an &lt;a href="http://halfhourhacks.blogspot.com/2007/10/so-good-its-bad.html"&gt;earlier post&lt;/a&gt;, in the Tetris game from WEP, the score overflows. I was able to repeat this a few times and it happened each time. Now, I've got it on camera (needed a lot of takes, so I have even more practice now).
&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/tetris/toverflow.html"&gt;Video&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
Look at the score.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-9109247557197089069?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/9109247557197089069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=9109247557197089069' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/9109247557197089069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/9109247557197089069'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/11/tetris-overflow-on-camera.html' title='Tetris Overflow - On Camera'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6351196361678213446</id><published>2007-11-12T17:40:00.001-08:00</published><updated>2008-03-08T14:39:52.716-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>201%</title><content type='html'>&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/RzkBOs8_GQI/AAAAAAAAAIs/U_JSRv35KfM/s1600-h/pain.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/RzkBOs8_GQI/AAAAAAAAAIs/U_JSRv35KfM/s320/pain.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5132134602462140674" /&gt;&lt;/a&gt;
(If you don't get it, it's a reference to a song).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6351196361678213446?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6351196361678213446/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6351196361678213446' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6351196361678213446'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6351196361678213446'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/11/201.html' title='201%'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_sIga-_J94mI/RzkBOs8_GQI/AAAAAAAAAIs/U_JSRv35KfM/s72-c/pain.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2592678924218050090</id><published>2007-11-10T12:33:00.001-08:00</published><updated>2008-03-10T18:42:26.204-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Collect'/><title type='text'>Directory</title><content type='html'>Last year, I wrote a DHTML app (in 30 minutes) for searching the directory of my college.
&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/RzYVvs8_GPI/AAAAAAAAAIk/HbPVP15noFY/s1600-h/shot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/RzYVvs8_GPI/AAAAAAAAAIk/HbPVP15noFY/s320/shot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5131312734700247282" /&gt;&lt;/a&gt;
&lt;br/&gt;
I parsed all of the data and wrote a Python script to turn it into a JavaScript array. With all of the student data in one JavaScript file, looking up information is extremely quick because everything is on the client and there is no server round-trip. So, I didn't even make a "Search" button - the results are returned as you type. 
&lt;br/&gt;
You can search by dorm and by floor, and send e-mails.
&lt;br/&gt;
This year, fellow students built upon this idea and created a Directory with even more features.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2592678924218050090?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2592678924218050090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2592678924218050090' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2592678924218050090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2592678924218050090'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/11/directory.html' title='Directory'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/RzYVvs8_GPI/AAAAAAAAAIk/HbPVP15noFY/s72-c/shot.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2309068579991700548</id><published>2007-11-03T11:35:00.000-07:00</published><updated>2010-08-31T21:49:35.392-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Collect'/><title type='text'>Pyget</title><content type='html'>I wrote a small program in Python for downloading files. (It is basically an interface for urllib, which does the real work). You can also use it from the command line with wget-like syntax.
&lt;br/&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/RyzCWT-zFoI/AAAAAAAAAIM/GDuoXr2R1Kw/s1600-h/pgmain.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/RyzCWT-zFoI/AAAAAAAAAIM/GDuoXr2R1Kw/s320/pgmain.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5128687764244731522" /&gt;&lt;/a&gt;
&lt;br/&gt;
It has some convenient features. If you have copied a url in the clipboard, it is automatically placed in the field, saving you a step. In the "Add..." dialog, you can specify a sequence of urls with the syntax "http://test.com/file_[0-4].zip", which expands into "http://test.com/file_0.zip", "http://test.com/file_1.zip", and so on. The most useful feature is downloading links from an html page:
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/RyzDKT-zFqI/AAAAAAAAAIc/zyXM653daBk/s1600-h/pginput.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/RyzDKT-zFqI/AAAAAAAAAIc/zyXM653daBk/s320/pginput.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5128688657597929122" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/RyzBgj-zFnI/AAAAAAAAAIE/nD1t1D2BTq8/s1600-h/pgpattern.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/RyzBgj-zFnI/AAAAAAAAAIE/nD1t1D2BTq8/s320/pgpattern.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5128686840826762866" /&gt;&lt;/a&gt;
&lt;br/&gt;
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/RyzCcz-zFpI/AAAAAAAAAIU/3-PEP--X-tM/s1600-h/pgsshot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/RyzCcz-zFpI/AAAAAAAAAIU/3-PEP--X-tM/s320/pgsshot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5128687875913881234" /&gt;&lt;/a&gt;

Here's the &lt;a href="http://neednom.com/bfisher/blog/pyget/pyget.zip"&gt;code&lt;/a&gt;. You'll need a recent version Python and the wxPython GUI toolkit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2309068579991700548?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2309068579991700548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2309068579991700548' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2309068579991700548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2309068579991700548'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/11/pyget.html' title='Pyget'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_sIga-_J94mI/RyzCWT-zFoI/AAAAAAAAAIM/GDuoXr2R1Kw/s72-c/pgmain.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4280242110066925649</id><published>2007-11-02T20:40:00.000-07:00</published><updated>2008-03-10T18:42:26.208-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Collect'/><title type='text'>Quoteboard</title><content type='html'>I wrote an online quoteboard from scratch. Using only my own code makes it lightweight, and I like being able to customize every detail.
&lt;br/&gt;
After 30 minutes planning and around 30 minutes of quick PHP/Mysql coding, I had a functional version of the site:

&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/Ryv9mj-zFkI/AAAAAAAAAHs/5bKUEfZomzg/s1600-h/quocr.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/Ryv9mj-zFkI/AAAAAAAAAHs/5bKUEfZomzg/s320/quocr.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5128471439626933826" /&gt;&lt;/a&gt;

Each quote also has a page where you can discuss it. Using xmlhttprequest, I added AJAX voting buttons that don't require a page refresh!
&lt;br/&gt;
Today, I added some styling, which took a while, but looks nicer:
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/Ryv-ED-zFlI/AAAAAAAAAH0/MgWcWr96AGw/s1600-h/quote.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/Ryv-ED-zFlI/AAAAAAAAAH0/MgWcWr96AGw/s320/quote.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5128471946433074770" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4280242110066925649?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4280242110066925649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4280242110066925649' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4280242110066925649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4280242110066925649'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/11/quoteboard.html' title='Quoteboard'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_sIga-_J94mI/Ryv9mj-zFkI/AAAAAAAAAHs/5bKUEfZomzg/s72-c/quocr.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-30181723482632948</id><published>2007-11-01T09:18:00.000-07:00</published><updated>2008-03-08T14:40:37.141-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>Nondeterministic Finite Automata</title><content type='html'>For an open-ended project in my Computer Science course, I wrote a program which will take any regular expression as an input and generate a NFA for recognizing that language. It can display this NFA graphically as well as generate Prolog code for simulating it and using it to match strings. (So, if you have Prolog, you could use this as an implementation of regular expressions.)
&lt;br/&gt;&lt;br/&gt;
I generate dot format files and call &lt;a href="http://www.graphviz.org"&gt;GraphViz&lt;/a&gt; to draw the graph. I use the symbol ',' to mean concatenation and enter 'a,a,b,b' instead of the traditional regexp 'aabb'. In the graphs, '.' refers to a null transition. Here are some sample inputs and outputs:
&lt;br/&gt;&lt;br/&gt;
a*,b|c
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/Ryn9Fj-zFeI/AAAAAAAAAG8/ZZvSQ0K5Gsc/s1600-h/fig1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/Ryn9Fj-zFeI/AAAAAAAAAG8/ZZvSQ0K5Gsc/s320/fig1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5127907922737829346" /&gt;&lt;/a&gt;

(a|b)*
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/Ryn9Kj-zFfI/AAAAAAAAAHE/IUDQfVQuTds/s1600-h/fig2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/Ryn9Kj-zFfI/AAAAAAAAAHE/IUDQfVQuTds/s320/fig2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5127908008637175282" /&gt;&lt;/a&gt;

(a|b),(a|b)
&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/Ryn9Oz-zFgI/AAAAAAAAAHM/8LVYHPbejQ4/s1600-h/fig3.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/Ryn9Oz-zFgI/AAAAAAAAAHM/8LVYHPbejQ4/s320/fig3.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5127908081651619330" /&gt;&lt;/a&gt;

(Note that my handling of * is slightly incorrect because the expression must be present at least once.) Disjunction uses the '|' symbol, for example 'a|b' means a or b. Unlimited levels of nested parenthesis are supported, thanks to recursion.
&lt;br/&gt;&lt;br/&gt;

I process the expression recursively by at each point dividing the input string into two parts, if possible. The string '(a|b),b' is split into (a|b) and b with the operation ',' , and the first part is then split into a and b with the operation |. If the expression is a or a*, then it will return, and work its way down. To my knowledge all complicated expressions are recognized successfully.
&lt;br/&gt;&lt;br/&gt;

The Python program then will output Prolog code that can be consulted. Once consulted, the predicate parse can be used with a list to see if the string is recognized by the language.
&lt;br/&gt;&lt;br/&gt;

Prolog code generated for (a|b),(a|b)
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;parse(L) :- start(S), 
    trans(S,L).

trans(X,[A&amp;#124;B]) :-
      delta(X,A,Y),
      write(X),
      write('  '),
      write([A&amp;#124;B]),
      nl,
      trans(Y,B).
      
trans(X,[A&amp;#124;B]) :-
      nulltrans(X,Y),
      trans(Y,[A&amp;#124;B]).
      
trans(X,[]):-
    nulltrans(X,Y),
    trans(Y,[]).
      
trans(X,[]) :- 
      final(X),
      write(X),
      write('  '),
      write([]), nl.

delta(0,a,1).
delta(0,b,2).
delta(3,a,4).
delta(3,b,5).
nulltrans(1,3).
nulltrans(2,3).
nulltrans(4,6).
nulltrans(5,6).
nulltrans(99,99).
start(0).
final(6).

&lt;/code&gt;&lt;/pre&gt;
Sample output of this Prolog code recognizing (a|b),(a|b):
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;#124; ?- parse([a,b]).
0  [a,b]
3  [b]
6  []

true ?

yes
&amp;#124; ?- parse([b,a]).
0  [b,a]
3  [a]
6  []

true ? 

(15 ms) yes
&amp;#124; ?- parse([b,a,b]).
0  [b,a,b]
3  [a,b]

no
&amp;#124; ?- parse([b]).
0  [b]

&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-30181723482632948?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/30181723482632948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=30181723482632948' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/30181723482632948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/30181723482632948'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/11/nondeterministic-finite-automata.html' title='Nondeterministic Finite Automata'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_sIga-_J94mI/Ryn9Fj-zFeI/AAAAAAAAAG8/ZZvSQ0K5Gsc/s72-c/fig1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1889198979389756035</id><published>2007-10-28T08:26:00.001-07:00</published><updated>2010-08-31T21:50:35.147-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Audio</title><content type='html'>Unlike most of the projects I've been working on, this one was related to a class. (But it is still thrown-together, undocumented, and uses several list comprehensions to write an audio effects in one complicated line of code). I'm in a course called Engineer's Orchestra where we build instruments and do physics. I was curious about how sound files on a computer work , so I set off learning a lot about digital audio. The first fun part was to learn the insides of a .wav file and write one starting from only bytes. Then, as a project for the class, I wrote a complete audio editor from scratch!&lt;br/&gt;
&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/RySqUj-zFdI/AAAAAAAAAG0/3PFdUSxcaVM/s1600-h/shot2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/RySqUj-zFdI/AAAAAAAAAG0/3PFdUSxcaVM/s320/shot2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5126409546087208402" /&gt;&lt;/a&gt;
&lt;br/&gt;
I wrote it in Python, because it would take me the least time to write it. My challenge was to write this without referencing any other code, like Audacity. This made it more fun.&lt;br/&gt;
&lt;br/&gt;
The GUI isn't very good, specifically because there are no other dialogs, but I have most of the important features covered: Add Silence Before, Add Silence After, Chop, Append File, Mix File, Modulate, Make Louder, Make Softer. And, it loads and saves standard wav files (16 bit or 8bit), so it is actually practical for editing things you have recorded.&lt;br/&gt;
&lt;br/&gt;
Then, I added a lot of audio effects. I would read the gist of an effect on Wikipedia and try to code it. This was even more fun, because if I got something wrong, it sometimes played very strange sounds. (I don't have any analog-style effects, like filters yet, but this is something I would really like to learn). In the GUI, you can use the following effects: Reverse, Octave up/down, Change pitch (the most challenging to think of how to write), Vibrato, Flanger, Echo, Reverb, Chorus, and Alien Voice. The alien voice modulates the samples with a sine wave - I came up with it while experimenting and don't know why it sounds strange.&lt;br/&gt;
&lt;br/&gt;
Listen to these (the wavs were generated by my program):&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/fx_vibrato.wav"&gt;Vibrato&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/fx_low.wav"&gt;Low&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/fx_reverb.wav"&gt;Reverb&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/fx_alien.wav"&gt;Alien Voice&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
Next, I added code to synthesize sounds. Sine, triangle, square, and sawtooth waves aren't that great by themselves, so I tried combinations of them to get the most interesting voices. The secret to making a sine wave sound better is to quietly mix in frequencies at that are slightly different. Surprisingly, the subtle beat frequencies make it sound expressive and musical. I worked a long time to create an electric organ-like instrument out of sine waves, and finally came up with something.&lt;br/&gt;
&lt;br/&gt;
Here are the results:&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/vx_organ.wav"&gt;Electric Organ&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/vx_smooth.wav"&gt;Smooth&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/vx_retro.wav"&gt;Retro&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/vx_red.wav"&gt;Red Noise&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/ylp/vx_sqrpha3.wav"&gt;Squarephase&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
Finally, I put all of the pieces together. I had an audio editor that can modify and arbitrarily change the pitches of sounds. So, I made a sampler! I wrote a command-line interface for a program where you can type in notes and play them. It parses commands like a4 into the note A440, or 4.f#5 into "play a dotted quarter note at F# 5". As an instrument, you can use anything - samples of your voice, a sample of a real instrument, your computer's MIDI, or even the same synthesized instruments like Electric Organ or Smooth.&lt;br/&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/RySqQT-zFcI/AAAAAAAAAGs/um_RyHApvgU/s1600-h/shot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/RySqQT-zFcI/AAAAAAAAAGs/um_RyHApvgU/s320/shot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5126409473072764354" /&gt;&lt;/a&gt;
&lt;br/&gt;
The program works well (the effects even work in both 16bit and 8bit sounds). &lt;br/&gt;
&lt;br/&gt;
Lest you think this program is polished, I must admit that it is not. This is not a joke, it is actually in the source:&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&lt;br/&gt;
manualsynth(audiodata, nsamples, _extend([[freq, .5*0.9**(8-1)]], [[freq*(i+1),.5*0.1*0.9**((8-1)-i)]  for i in range(1,8)],[[freq*1.00606, .5*0.9**(8-1)]], [[freq*1.00606*(i+1),.5*0.1*0.9**((8-1)-i)]  for i in range(1,8)]))&lt;br/&gt;
&lt;br/&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;
I should get around to fixing that sometime.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1889198979389756035?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1889198979389756035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1889198979389756035' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1889198979389756035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1889198979389756035'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/audio.html' title='Audio'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/RySqUj-zFdI/AAAAAAAAAG0/3PFdUSxcaVM/s72-c/shot2.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6155842445484537853</id><published>2007-10-27T15:15:00.000-07:00</published><updated>2010-08-31T21:51:32.250-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Speak'/><title type='text'>Quick Unicode Entry</title><content type='html'>This project doesn't have any direct use now, but it was fun to write, and might be useful for writing letters in Spanish. I can simply hit Alt-A to get an a with an accent, Alt-Shift-1 to get &amp;#161;, and define any other combination I want.&lt;br/&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/RyO5wj-zFZI/AAAAAAAAAGU/ozRfqHL4sLk/s1600-h/sample.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/RyO5wj-zFZI/AAAAAAAAAGU/ozRfqHL4sLk/s320/sample.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5126145044821251474" /&gt;&lt;/a&gt;
&lt;br/&gt;
This program is a lightweight text editor intended for writing text in other languages. Most word processors have a "Insert Symbol" option for inserting a character, but this process is too slow. If you do all of your typing in another language, one can set the system language, but this will be a system-wide change and is not very customizable. In this program, on the other hand, it is simple to set up your own keymaps and choose what keys create which characters. One can also conveniently visualize these bindings.&lt;br/&gt;

&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/RyO53T-zFaI/AAAAAAAAAGc/eQ9EvjiTlrU/s1600-h/vis.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/RyO53T-zFaI/AAAAAAAAAGc/eQ9EvjiTlrU/s320/vis.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5126145160785368482" /&gt;&lt;/a&gt;

&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/RyO5_T-zFbI/AAAAAAAAAGk/I9DwEbxBST4/s1600-h/vis2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/RyO5_T-zFbI/AAAAAAAAAGk/I9DwEbxBST4/s320/vis2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5126145298224321970" /&gt;&lt;/a&gt;

&lt;br/&gt;
Because some languages require access to more than 26 symbols, this program uses the concept of "modes." For example, the default keymap has a mode called Grave Accent. In this mode, typing a vowel like o will produce o with a grave accent. The program begins in Normal Mode, but you can press Control+L to enter Grave Accent mode. View the available modes for the current keymap by choosing List Modes from the Characters menu.&lt;br/&gt;
&lt;br/&gt;
When you enter a mode, you stay in that mode until you press the key combination, typically Control+Space, to return to Normal mode.&lt;br/&gt;
&lt;br/&gt;
Edit the current keymap by choosing "Edit key bindings" from the Characters menu. (Changes take place when the mode is selected from the Characters menu). Create a new map by creating a .py.js file in the keymaps directory.&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://neednom.com/bfisher/blog/unicode/unicode.zip"&gt;The Code&lt;/a&gt;. Unzip and run "main.py." Works in Python 2.5. Linux is not yet supported, as it seems the keycodes are different.&lt;br/&gt;
&lt;br/&gt;
I spent some time coming up with good keymaps. It comes with a map for characters in Latin-1 and a very useful Greek map.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6155842445484537853?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6155842445484537853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6155842445484537853' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6155842445484537853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6155842445484537853'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/quick-unicode-entry.html' title='Quick Unicode Entry'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_sIga-_J94mI/RyO5wj-zFZI/AAAAAAAAAGU/ozRfqHL4sLk/s72-c/sample.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1844022987386539361</id><published>2007-10-24T01:28:00.000-07:00</published><updated>2008-03-08T14:45:00.346-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>Interesting Facts in Biology</title><content type='html'>Did you know?
&lt;ul&gt;
&lt;li&gt;Slugs are gastropods that have white flowers for a specific tissue.&lt;/li&gt;
&lt;li&gt;This has produced a nuclear matrix secreted into the bird’s plasma membrane.&lt;/li&gt;
&lt;li&gt;Individuals associate with polar molecules such as that of the worm through the mitochondrial and chloroplast genomes of the urethra.&lt;/li&gt;
&lt;/ul&gt;
I wrote a program that will analyze and automatically summarize Biology. (Very useful). It's a web app, but is on Olin's server right now. 

&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/Rx8CZLF8TlI/AAAAAAAAAFw/JoxTKISnx3s/s1600-h/main.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/Rx8CZLF8TlI/AAAAAAAAAFw/JoxTKISnx3s/s320/main.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5124817532468612690" /&gt;&lt;/a&gt;

&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/Rx8Cd7F8TmI/AAAAAAAAAF4/m95tRmH5vO4/s1600-h/inaction.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/Rx8Cd7F8TmI/AAAAAAAAAF4/m95tRmH5vO4/s320/inaction.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5124817614072991330" /&gt;&lt;/a&gt;

I compiled a huge amount of Biology information and made a Markov model. I did a lot of work in Python stripping unwanted data. At first, I was putting all of the data in a php file, but this turned out to be inefficient and a heavy load on the server. I ended up with a solution: I divided all of the text into 64 slices, and then created 8 medium-sized files out of shuffled slices (for variety). I then wrote Python code to generate one Markov model for each of the files and save the results as a Javascript object .js file. Then, the php code randomly picks one of the 8 .js files to include whenever the page is loaded. The Markov generation is all client-side, so you can generate many times without needing a page refresh.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1844022987386539361?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1844022987386539361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1844022987386539361' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1844022987386539361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1844022987386539361'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/interesting-facts-in-biology.html' title='Interesting Facts in Biology'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/Rx8CZLF8TlI/AAAAAAAAAFw/JoxTKISnx3s/s72-c/main.PNG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-8173098839096876607</id><published>2007-10-21T11:57:00.000-07:00</published><updated>2010-08-31T21:53:31.139-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>Computer Science + Lolcats</title><content type='html'>I did some photoshopping last week :)
&lt;br/&gt;
&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/RxuhnrF8TkI/AAAAAAAAAFo/zlxwL_qjiDU/s1600-h/turing+cat.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/RxuhnrF8TkI/AAAAAAAAAFo/zlxwL_qjiDU/s320/turing+cat.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5123866704018689602" /&gt;&lt;/a&gt;

&lt;br/&gt;
If you've taken a Computer Science course, you may find &lt;a href="http://neednom.com/bfisher/blog/lolfocs.pdf"&gt;this&lt;/a&gt; humorous. It's my Powerpoint presentation of Computer Science + Lolcats.
&lt;br/&gt;&lt;br/&gt;
(Feel free to copy these but give me credit).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-8173098839096876607?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/8173098839096876607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=8173098839096876607' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8173098839096876607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8173098839096876607'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/computer-science-lolcats.html' title='Computer Science + Lolcats'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/RxuhnrF8TkI/AAAAAAAAAFo/zlxwL_qjiDU/s72-c/turing+cat.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-8349265448479134586</id><published>2007-10-13T19:22:00.000-07:00</published><updated>2008-03-08T14:41:45.627-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Beep</title><content type='html'>This weekend I thought it would be cool to learn about digital audio. Specifically, I wanted to look inside of audio files and know how they work on the level of bytes. Also, I'm going to do my exploring without looking at other source, like Audacity, to make things more interesting.
&lt;br/&gt;&lt;br/&gt;
It turns out that digital audio files are split up into frames that are typically either 8-bits or 16-bits. So a wave file is essentially list of 8-bit or 16-bit numbers that represent the pressure on the microphone. The frame rate dictates how many of these frames go by per second.
&lt;br/&gt;&lt;br/&gt;
In thirty minutes, I was successfully able to come up with code for creating a .wav file at any frequency:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;import wave
import math
import array

#Create a 16 bit wave
width = 2 #16 bits is 2 bytes
maxval = 65535
sample_rate=44100
channels = 1
samples = array.array('h') #Signed short

# Create a sine wav, of frequency fFreq
nSamples = sample_rate*2 #two seconds long
fFreq = 440
w = fFreq*2*math.pi / sample_rate
amplitude = 65535/2 #Loudest amplitude
samples.extend( (amplitude * math.sin(w*x)) for x in xrange(nSamples) )

f = wave.open('c:\\a440.wav', 'wb')
f.setnchannels(channels)
f.setframerate(sample_rate)
f.setsampwidth(width)
f.writeframes(samples.tostring())
f.close()
&lt;/code&gt;&lt;/pre&gt;
Some of my ideas: The array type works perfectly for storing the samples - it is far more efficient than a Python list. Note also that I am using a "generator expression", which works similarly to a list comprehension but does not need to actually create the list. In the future I plan on using generators for creating the signals.
&lt;br/&gt;&lt;br/&gt;
It is weird that 16-bit sound is in the "signed short" format, instead of being unsigned.
&lt;br/&gt;&lt;br/&gt;
On a less serious note, I then saw the beep method winsound module, and was amused:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;import winsound
# so retro!
winsound.Beep(440,10)
winsound.Beep(600,10)
#bonus stage!
for i in range(40,400,30):
    winsound.Beep(i,90)
&lt;/code&gt;&lt;/pre&gt;
There's no way to control the volume, though, so this can get annoying.
&lt;br/&gt;&lt;br/&gt;
Next I'll be writing some sweet audio effects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-8349265448479134586?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/8349265448479134586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=8349265448479134586' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8349265448479134586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8349265448479134586'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/beep.html' title='Beep'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-8445638197077466204</id><published>2007-10-11T23:17:00.000-07:00</published><updated>2010-08-31T21:54:02.549-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='See'/><title type='text'>Recursive Shapes v0.2</title><content type='html'>This project took far longer than half an hour, but it's pretty cool.&lt;br/&gt;
&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/Rw8TUFk-rlI/AAAAAAAAAFg/cy7dUmzV_UM/s1600-h/sshot.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/Rw8TUFk-rlI/AAAAAAAAAFg/cy7dUmzV_UM/s320/sshot.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5120332537159134802" /&gt;&lt;/a&gt;


&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/Rw8TB1k-rjI/AAAAAAAAAFQ/2spwy18eOuI/s1600-h/tr.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/Rw8TB1k-rjI/AAAAAAAAAFQ/2spwy18eOuI/s200/tr.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5120332223626522162" /&gt;&lt;/a&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/Rw8S-1k-riI/AAAAAAAAAFI/vHCygn_IpdE/s1600-h/sq.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/Rw8S-1k-riI/AAAAAAAAAFI/vHCygn_IpdE/s200/sq.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5120332172086914594" /&gt;&lt;/a&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/Rw8S71k-rhI/AAAAAAAAAFA/qlioZcKyOUg/s1600-h/l.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/Rw8S71k-rhI/AAAAAAAAAFA/qlioZcKyOUg/s200/l.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5120332120547307026" /&gt;&lt;/a&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/Rw8S31k-rgI/AAAAAAAAAE4/F5N4k00qrrU/s1600-h/c.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/Rw8S31k-rgI/AAAAAAAAAE4/F5N4k00qrrU/s200/c.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5120332051827830274" /&gt;&lt;/a&gt;
&lt;a href="http://bp2.blogger.com/_sIga-_J94mI/Rw8Sw1k-rfI/AAAAAAAAAEw/m7hNTHXEFp0/s1600-h/b.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_sIga-_J94mI/Rw8Sw1k-rfI/AAAAAAAAAEw/m7hNTHXEFp0/s200/b.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5120331931568745970" /&gt;&lt;/a&gt;
&lt;br/&gt;While doodling in Bio, I thought of a way to programmatically draw any recursively-defined shape. By this, I mean any shape that contains a scaled-down version of the entire shape. This is a property found in many fractals.
&lt;br/&gt;
&lt;br/&gt;Traditionally, and in all of the fractal-drawing software of this type I've seen, the shapes are defined with turtle-style commands (turn right, draw a line, turn left, and so on). There exists a specific system called L-System that can define recursive pictures, and with a lot of work can probably define everything my program can, with the exception of curves. However, when looking at the shape, I do not always find the long list of turtle instructions or L-System rules to be intuitive. I decided to instead use traditional Euclidean x and y coordinates, and Python code as the rule defining the next iteration. This way, I can sketch out a shape on a piece of paper and enter its code very quickly and clearly. It is much easier to me to enter a shape in terms of x and y.
&lt;br/&gt;
&lt;br/&gt;I had an idea for drawing curved lines that turned out very well. The code actually draws an arc of a circle, but positioned so that the arc passes through the two points. A parameter specifies how much curvature should be given.
&lt;br/&gt;
&lt;br/&gt;Looking at the still images is only half of it: I added an easy way to draw animations! There are a lot of really cool animations defined in the samples. (Play them by removing the # and pressing draw.)
&lt;br/&gt;
&lt;br/&gt;Here is the &lt;a href="http://neednom.com/bfisher/blog/recursive/recursive.zip"&gt;code&lt;/a&gt; (71k). (Requires Python 2.5, cross-platform). All you need to do is unzip the file, open main.py, load a shape from the shapes menu and start playing with it! The argument to the .draw() method is the number of iterations (don't make this too big). You are encouraged to tweak the code or create your own shapes.
&lt;br/&gt;
I wrote this just for fun, and it's (unfortunately) not connected with anything I'm doing at school.
&lt;br/&gt;Planned for the next version:
&lt;br/&gt;Drawing images, ellipses, text. Save images as PNG files, anti-alias option. Making variable names consistent... and a revolutionary improvement that is still in the works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-8445638197077466204?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/8445638197077466204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=8445638197077466204' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8445638197077466204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8445638197077466204'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/recursive-shapes-v02.html' title='Recursive Shapes v0.2'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/Rw8TUFk-rlI/AAAAAAAAAFg/cy7dUmzV_UM/s72-c/sshot.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7795461241856248347</id><published>2007-10-07T14:00:00.000-07:00</published><updated>2008-03-08T14:44:07.365-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>So Good It's Bad!</title><content type='html'>I was playing Windows Entertainment Pack Tetris. After the 10th level, it doesn't get any faster (of course it is fast enough) - and so I was playing for a while, when suddenly I noticed that I had a negative score!!! The integer representing my score had overflowed!

&lt;a href="http://bp1.blogger.com/_sIga-_J94mI/RwlWRFk-rdI/AAAAAAAAAEg/2U5gYJLPT4k/s1600-h/tetrisnegative01.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/RwlWRFk-rdI/AAAAAAAAAEg/2U5gYJLPT4k/s320/tetrisnegative01.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5118717303038324178" /&gt;&lt;/a&gt;

&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/RwlWW1k-reI/AAAAAAAAAEo/UwlShI51pNc/s1600-h/tetrisnegative02.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/RwlWW1k-reI/AAAAAAAAAEo/UwlShI51pNc/s320/tetrisnegative02.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5118717401822572002" /&gt;&lt;/a&gt;

The Windows Entertainment Pack version of Tetris must display your score as a signed 16-bit integer (I think it's even a 16 bit program). Because of 2s complement, an integer will after being large positive will be large negative. The number still sorted as larger than the other scores, and counted as a high score, though. Cool.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7795461241856248347?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7795461241856248347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7795461241856248347' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7795461241856248347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7795461241856248347'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/so-good-its-bad.html' title='So Good It&apos;s Bad!'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/RwlWRFk-rdI/AAAAAAAAAEg/2U5gYJLPT4k/s72-c/tetrisnegative01.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-2625677194989407163</id><published>2007-10-03T16:22:00.000-07:00</published><updated>2008-03-10T18:44:26.467-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>Unreadable Python</title><content type='html'>&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;
exec(&amp;quot;)))'YLRJHOUVO[`WYLO[VUH',dro(pam,)52+c(rhc:c adbmal(pam,__dda__.rts(ecuder tnirp&amp;quot;[::-1])

&lt;/code&gt;&lt;/pre&gt;

Actually, I could have made it a lot worse. But it was still fun.

Figured it out? Try this one:

&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;
print reduce(str.__add__,map(lambda c:chr(c+25),[(72-sum([ord(c)-74 for c in '=IEVM=LAOVCKPQHBP='][:i])) for i in range(19)]))


&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-2625677194989407163?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/2625677194989407163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=2625677194989407163' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2625677194989407163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/2625677194989407163'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/10/unreadable-python.html' title='Unreadable Python'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3632687588249072453</id><published>2007-09-28T23:02:00.000-07:00</published><updated>2010-08-31T21:54:42.428-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='See'/><title type='text'>Periodic Generator</title><content type='html'>A few years ago, I saw a t-shirt that spelled "WWU Chemistry" in the style of a periodic table. So, I decided to throw together a DHTML program to spell an arbitrary phrase this way. Today, I cleaned up the code a bit and added sample phrases. The algorithm prefers two-letter combinations to one letter. Unfortunately, a lot of common letters can't be done exactly, so I had to be a bit creative. 

&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/Rv3ql7tV2lI/AAAAAAAAAEY/8yg07Y6nKlk/s1600-h/hhhacks.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/Rv3ql7tV2lI/AAAAAAAAAEY/8yg07Y6nKlk/s320/hhhacks.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5115502689166678610" /&gt;&lt;/a&gt;

Here's the &lt;a href="http://neednom.com/bfisher/blog/periodic/periodic.html"&gt;link&lt;/a&gt;. Unfortunately, to flip letters I have to use a Filter that only works in Internet Explorer; there's no way to do this in Firefox.
&lt;br/&gt;&lt;br/&gt;
Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3632687588249072453?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3632687588249072453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3632687588249072453' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3632687588249072453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3632687588249072453'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/09/periodic-generator.html' title='Periodic Generator'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/Rv3ql7tV2lI/AAAAAAAAAEY/8yg07Y6nKlk/s72-c/hhhacks.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4925293088845513710</id><published>2007-09-24T15:14:00.000-07:00</published><updated>2010-08-31T21:55:10.014-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>JavaScript and Logic</title><content type='html'>&lt;p&gt;Two years ago, I was in a logic course at WWU. The class was interesting, but some of the assigned online work was a bit repetitive. I did enjoy proving theorems, but filling out the truth tables was not exciting.&lt;/p&gt;

&lt;p&gt;So, after some thinking, I quickly wrote a script called "logic.js" which can evaluate prepositional logic. I thought of how to do this from scratch (looking back on it there are probably better ways to do evaluate infix notation). For compound expressions, the idea was to use regex to find the innermost operation and evaluate it, replacing it with the result. Then this process would be repeated until there was only one value.&lt;/p&gt;

&lt;p&gt;I used the engine to &lt;a href="http://neednom.com/bfisher/blog/logic/truthtable.html"&gt;Draw Truth Tables&lt;/a&gt; and &lt;a href="http://neednom.com/bfisher/blog/logic/logic.html"&gt;Evaluate Logic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For fun, I wrote a similar program for a ProbStat course, at around the same time, for &lt;a href="http://neednom.com/bfisher/blog/logic/set/index.html"&gt;Set Diagrams&lt;/a&gt;. None of these are "finished" finished, but they work well enough.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4925293088845513710?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4925293088845513710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4925293088845513710' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4925293088845513710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4925293088845513710'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/09/javascript-and-logic.html' title='JavaScript and Logic'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6197498785490820317</id><published>2007-09-24T00:09:00.000-07:00</published><updated>2010-08-31T21:56:13.437-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>C# Is Useful</title><content type='html'>For throwing together a GUI app, C#.NET is actually pretty handy. Here is my &lt;a href="http://neednom.com/bfisher/blog/vcsharp/vcsharp.htm"&gt;demo&lt;/a&gt; proving that C# isn't all bad - that is pretty quick and effective. Note that you don't need to use the visual designer at all, and that is what I would do for a larger scale project.
&lt;br/&gt;
For larger scale projects, XAML is pretty interesting too. I used it at work this summer.
&lt;br/&gt;
Why I like it:
&lt;ul&gt;
&lt;li&gt;Compilation step isn't a hindrance - it is fast and catches many mistakes.&lt;/li&gt;
&lt;li&gt;GUI designer is helpful but it's easy to code your own layout.&lt;/li&gt;
&lt;li&gt;.NET library comes with a lot.&lt;/li&gt;
&lt;li&gt;Sweet language features make it feel more flexible and fun than Java.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;
On the other hand, here are the reasons why I still believe .NET is not the answer:
&lt;ul&gt;
&lt;li&gt;Not really portable to Linux. (Yes, Mono, but still).&lt;/li&gt;
&lt;li&gt;Requires a huge framework. There isn't a way to "static link" your programs. This means that Joe User might have to download lots of megabytes to even run your program, which I know I would be reluctant to do. And what if he is on dial-up?&lt;/li&gt;
&lt;li&gt;Memory management and performance - it's pretty good, but watch out for certain things (GC, deep inheritance, reflection, and UI drawing). It is sometimes hard for me to accept that: "My C# program looks and behaves the same as one written in C++. But it is slower and takes more memory." The only answer is developer time. Of course, I am glad that there are simpler languages like C#, and they have benefited me, but this tradeoff is probably partially responsible for today's software bloat.
&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6197498785490820317?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6197498785490820317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6197498785490820317' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6197498785490820317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6197498785490820317'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/09/c-is-useful.html' title='C# Is Useful'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3516198903276413785</id><published>2007-09-20T21:18:00.000-07:00</published><updated>2010-08-31T21:56:59.000-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>Tetris</title><content type='html'>Bored? Time for Tetris. 1990 Style.&lt;br/&gt;
&lt;a href="http://bp3.blogger.com/_sIga-_J94mI/RvNGKbtV2kI/AAAAAAAAAEQ/V3AIs8bUFEo/s1600-h/tetrislogo.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_sIga-_J94mI/RvNGKbtV2kI/AAAAAAAAAEQ/V3AIs8bUFEo/s320/tetrislogo.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5112507147046214210" /&gt;&lt;/a&gt;
High Score: 25,613 (good)&lt;br/&gt;
Favorite Move: The Spear (&lt;a href="http://neednom.com/bfisher/blog/tetris/Tetris.htm"&gt;See it&lt;/a&gt;)&lt;br/&gt;
Windows Entertainment Pack Tetris is where it's at.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3516198903276413785?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3516198903276413785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3516198903276413785' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3516198903276413785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3516198903276413785'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/09/tetris.html' title='Tetris'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_sIga-_J94mI/RvNGKbtV2kI/AAAAAAAAAEQ/V3AIs8bUFEo/s72-c/tetrislogo.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6254007612650218264</id><published>2007-09-19T22:55:00.000-07:00</published><updated>2010-08-31T21:57:12.601-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Audacity, and Phonemes</title><content type='html'>A friend, Chris, discovered that some songs sound pretty interesting backwards. You can easily open up an mp3 in Audacity and use the "Reverse" effect to try it out.
&lt;br/&gt;
&lt;br/&gt;Good ones:
&lt;br/&gt;Hot Butter - &lt;a href="http://neednom.com/bfisher/blog/reverse/popcorn.mp3"&gt;Popcorn&lt;/a&gt; (0.28Mb)
&lt;br/&gt;The Postal Service - &lt;a href="http://neednom.com/bfisher/blog/reverse/district.mp3"&gt;The District Sleeps Alone&lt;/a&gt; (0.56Mb)
&lt;br/&gt;Stryper -  &lt;a href="http://neednom.com/bfisher/blog/reverse/stryper.mp3"&gt;To Hell With the Devil&lt;/a&gt; (0.49Mb)
&lt;br/&gt;Queen - &lt;a href="http://neednom.com/bfisher/blog/reverse/dust.mp3"&gt;Another One Bites the Dust&lt;/a&gt; (0.49Mb)
&lt;br/&gt;
&lt;br/&gt;By the way, the Postal Service and Sinatra go together kind of nicely when you mash them together with Audacity. And of course, Audacity has the wah-wah effect which is good for just about everything.
&lt;br/&gt;
&lt;br/&gt;Also, this summer, I was using a mic to record my voice to get the formant frequencies of my vowels, in order to synthesize my speech. My brother Tim thought of a fun game to play instead:
&lt;br/&gt;
&lt;br/&gt;1) Using Audacity, record yourself saying a simple phrase.
&lt;br/&gt;2) Reverse the recorded sound and listen to the weird noises.
&lt;br/&gt;3) Record yourself try to make those same noises. 
&lt;br/&gt;4) Reverse that recording and see if you can understand it!
&lt;br/&gt;(Repeat)
&lt;br/&gt;
&lt;br/&gt;It helps to say the phrase very slowly and precisely. We tried to say "Fisher brothers", but it kept on coming out as "Fee-sher Brogleez" :)
&lt;br/&gt;
&lt;br/&gt;The game is challenging because our ears behave strangely when interpreting language, especially vowel sounds. For example, in English many vowel sounds are dipthongs, but mentally we hear them as one unit. I was thinking about this in the car one day. Say the word "high." That is one vowel, right? Wrong. You are actually saying "Ha - eeee" quickly. Same for the sound in "day". That is "deh - eeee". Is that not the weirdest thing? Think about it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6254007612650218264?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6254007612650218264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6254007612650218264' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6254007612650218264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6254007612650218264'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/09/audacity-and-phonemes.html' title='Audacity, and Phonemes'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-1854742623790962843</id><published>2007-09-19T21:05:00.000-07:00</published><updated>2008-03-08T14:51:13.193-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>TweakUI, RTF</title><content type='html'>I used to Regedit most of my Windows customizations. But now, the TweakIU program provides a GUI for many common changes. Here are some of my favorites:
&lt;br/&gt;
Read the "tips." There will be some handy keyboard shortcuts. (Did you know you can hold down Control to select many taskbar items? Also, when confirming file actions, Shift+No will mean "No to All."
&lt;br/&gt;
Under Internet Explorer, you can set the loading-page animation, which I used to great effect. (When I use IE, now I have a running Mario icon when a page loads). I also changed a registry setting so that "Internet Explorer, brought to you by Mario" is in the title of each window.
&lt;br/&gt;
Finally, under Templates you can edit the entries in the Windows "New file" menu. (This is under the file extension in HKEY_CLASSES_ROOT). I added a "Rich Text Document RTF" entry by pointing to a new .rtf file on my hard drive. Wordpad can be a good lightweight editor if you want simple formatted text. (And, you can use Paste Special/Unformatted to strip formatting). So I always associate .rtf files with Wordpad.
&lt;br/&gt;
No, rtfm is not a manual in rtf format. It means something else.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-1854742623790962843?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/1854742623790962843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=1854742623790962843' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1854742623790962843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/1854742623790962843'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/09/tweakui-rtf.html' title='TweakUI, RTF'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-6759430189493491495</id><published>2007-09-08T20:29:00.000-07:00</published><updated>2008-03-08T14:46:51.138-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='See'/><title type='text'>Pointillism</title><content type='html'>Facebook has a "Graffiti" application where you can draw pictures on a profile by clicking and dragging. Because art is not my forte, I decided to find a way to paste an actual bitmap image in the space.
&lt;br/&gt;
&lt;br/&gt;First, I made a grayscale image of my face. I wrote a little Python script using the Python Imaging Library to make a list of all pixel coordinates that were white. I then wrote an AutoIt script to simulate mouse-clicks at all of those screen locations (hacktastic).
&lt;br/&gt;
&lt;br/&gt;The results:
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/RuNpAB6vKaI/AAAAAAAAAEI/rOZttbL9k-s/s1600-h/result.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/RuNpAB6vKaI/AAAAAAAAAEI/rOZttbL9k-s/s320/result.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5108041851603986850" /&gt;&lt;/a&gt;
&lt;br/&gt;Not perfect, but still visible.
&lt;br/&gt;AutoIt is a pretty cool program for Windows automation. It can simulate keyboard and mouse events.
&lt;br/&gt;
&lt;br/&gt;The actual reason I did this is that I wanted to learn about image processing in Python. It's usually not trivial in most languages / libraries to get pixel access of a PNG image, but PIL seems to do well. Stay tuned for my upcoming ultimate Image Processing lab.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-6759430189493491495?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/6759430189493491495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=6759430189493491495' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6759430189493491495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/6759430189493491495'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/09/pointillism.html' title='Pointillism'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_sIga-_J94mI/RuNpAB6vKaI/AAAAAAAAAEI/rOZttbL9k-s/s72-c/result.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-219307662504936279</id><published>2007-08-28T02:32:00.001-07:00</published><updated>2009-01-09T19:13:35.592-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>Lame</title><content type='html'>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:&lt;br/&gt;

&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;
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
&lt;/code&gt;&lt;/pre&gt;

Put the batch file someplace accessible. To encode some songs, all you need to do is drag and drop them onto the batch file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-219307662504936279?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/219307662504936279/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=219307662504936279' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/219307662504936279'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/219307662504936279'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/lame.html' title='Lame'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-3978473669018552174</id><published>2007-08-24T10:28:00.000-07:00</published><updated>2008-03-08T14:47:34.731-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Play'/><title type='text'>A n00b's RGB song</title><content type='html'>Written at around 3 am:&lt;br/&gt;
(Sung to the tune of "abc"/Twinkle Twinkle Little Star)&lt;br/&gt;&lt;br/&gt;

"0 1 2 3 4 5 6&lt;br/&gt;
7 8 9 A B-C-D-E-F&lt;br/&gt;
Sharp o o o o o Black&lt;br/&gt;
Red Green Blue White Web Safe hack&lt;br/&gt;
Now I know my RGBs&lt;br/&gt;
I can be a hacker please?"&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-3978473669018552174?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/3978473669018552174/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=3978473669018552174' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3978473669018552174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/3978473669018552174'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/rgbs.html' title='A n00b&apos;s RGB song'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7855860807398283484</id><published>2007-08-21T23:28:00.000-07:00</published><updated>2010-08-31T22:54:27.321-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Speak'/><title type='text'>Speed Reading</title><content type='html'>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.&lt;br/&gt;&lt;br/&gt;

&lt;a href="http://neednom.com/bfisher/blog/speedread/cool.html" target="new"&gt;Speed read&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
Change the number to change the delay between words. (I think the idea is to start at a comfortable level and gradually get faster).&lt;br/&gt;&lt;br/&gt;

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).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7855860807398283484?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7855860807398283484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7855860807398283484' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7855860807398283484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7855860807398283484'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/speed-reading.html' title='Speed Reading'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5136881293556641540</id><published>2007-08-21T23:04:00.000-07:00</published><updated>2008-03-08T14:48:59.108-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>Python Ostream</title><content type='html'>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 &amp;lt;&amp;lt; x" in Python. I liked the idea so I put together a simpler and cleaner implementation.&lt;br/&gt;&lt;br/&gt;

This is Python code (!):&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;cout &amp;lt;&amp;lt; &amp;quot;hello, world&amp;quot; &amp;lt;&amp;lt; nl
cout &amp;lt;&amp;lt; &amp;quot;Tuples:&amp;quot; &amp;lt;&amp;lt; (5 , 6, 7) &amp;lt;&amp;lt; nl
cout &amp;lt;&amp;lt; &amp;quot;Lists:&amp;quot; &amp;lt;&amp;lt; [5 , 6, 7] &amp;lt;&amp;lt; nl
    
cout &amp;lt;&amp;lt; &amp;quot;The number, &amp;quot; &amp;lt;&amp;lt; 1+1 &amp;lt;&amp;lt; &amp;quot;, bigger than &amp;quot; &amp;lt;&amp;lt; 1.0/5.0 &amp;lt;&amp;lt; nl
print &amp;quot;The number, %s, bigger than %s&amp;quot; % (1+1, 1.0/5.0)
print &amp;quot;The number,&amp;quot; , 1+1 , &amp;quot;, bigger than&amp;quot; , 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.
&lt;/code&gt;&lt;/pre&gt;

and here is the class (concise, and you can even pass it another stream object):&lt;br/&gt;
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;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'
&lt;/code&gt;&lt;/pre&gt;

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.&lt;br/&gt;&lt;br/&gt;

Have you ever thought it is weird that left-shift means print? Actually, cout &lt;&lt; "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 &lt;&lt; 4 does mylist.append(4) ... and mylist &gt;&gt; 0 does mylist.pop(0). (Just kidding).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5136881293556641540?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5136881293556641540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5136881293556641540' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5136881293556641540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5136881293556641540'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/python-ostream.html' title='Python Ostream'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-8510437801368321612</id><published>2007-08-10T13:07:00.000-07:00</published><updated>2008-03-08T14:46:51.141-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='See'/><title type='text'>Change Desktop Pattern</title><content type='html'>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 &lt;a href="http://www.oreilly.com/pub/h/5091"&gt;here&lt;/a&gt; (I guess someone else had thought of this before me), and so I was ready. Note: I haven't tested this in Vista.&lt;br/&gt;

To set it up,&lt;br/&gt;
1) Create a folder, and a subfolder named "bgtemp".&lt;br/&gt;&lt;br/&gt;
2) Enter the following and save it as "go.bat", within the folder:
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;echo &amp;quot;Change of Wallpaper - Ben Fisher&amp;quot;
@echo off
del bgtemp\*.bmp
cscript /NoLogo changed.wsf
&lt;/code&gt;&lt;/pre&gt;
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.
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;job id=&amp;quot;ChangeBG&amp;quot;&amp;gt;
&amp;lt;script language=&amp;quot;JScript&amp;quot;&amp;gt;
fs = new ActiveXObject(&amp;quot;Scripting.FileSystemObject&amp;quot;);
var fso, e, file;
var strName; 
var aNames = [];
var strPath = &amp;quot;C:\\Program Files\\Ben's Fractal Screensaver\\Media&amp;quot;;
var strDestination = &amp;quot;C:\\Program Files\\Ben's Fractal Screensaver\\Script\\bgtemp\\&amp;quot;;
fso = new ActiveXObject(&amp;quot;Scripting.FileSystemObject&amp;quot;);
e = new Enumerator(fso.GetFolder(strPath).files);
for (e.moveFirst(); ! e.atEnd(); e.moveNext()) 
{
  file = e.item();
  strName = file.name.toString();
  if (strName.indexOf(&amp;quot;.jpg&amp;quot;) &amp;gt; 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(&amp;quot;Gfx.Converter&amp;quot;);
Converter.ToBitmap( fullpath, strDestination );

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

// Refresh, credit to http://www.oreilly.com/pub/h/5091
objShell.Run(&amp;quot;%windir%\\System32\\RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters&amp;quot;, 1, false);
&amp;lt;/script&amp;gt;
&amp;lt;/job&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
4) You'll also need the "convert to bitmap" ActiveX control from &lt;a href="http://dev.remotenetworktechnology.com/wsh/comwsh.htm"&gt;http://dev.remotenetworktechnology.com/wsh/comwsh.htm&lt;/a&gt;. 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.&lt;br/&gt;&lt;br/&gt;

5) Put a shortcut to "go.bat" in your startup folder. c:\Documents and Settings\(you)\Start Menu\Programs\Startup.&lt;br/&gt;

And that's it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-8510437801368321612?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/8510437801368321612/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=8510437801368321612' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8510437801368321612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/8510437801368321612'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/change-desktop-pattern.html' title='Change Desktop Pattern'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7285111147597217754</id><published>2007-08-10T12:19:00.000-07:00</published><updated>2010-08-31T22:55:55.554-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hear'/><title type='text'>System Sounds!</title><content type='html'>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.&lt;br/&gt;

My computer is now Incredibly Awesome!
&lt;table&gt;
&lt;tr&gt;&lt;td&gt;Start Windows&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/gloria.wav"&gt;Gloria&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Windows Logon&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/ominous.wav"&gt;Berg - Violin Concerto Mv. III Allegro&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Exit Windows&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/dont.wav"&gt;Grandmaster Flash - The Message&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Windows Logoff&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/thatsound.wav"&gt;Edgar Winter - Frankenstein&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Device Connect&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/bell.wav"&gt;Javanese Bell Excerpt&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Device Disconnect&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/bellout.wav"&gt;Javanese Bell Excerpt&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Program Error&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/oww.wav"&gt;James Brown - Living in America&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Critical Stop&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/bassbass.wav"&gt;Yes - City of Love&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Asterisk&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/cage.wav"&gt;John Cage - Sonatas and Interludes for Prepared Piano&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Default Beep&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/flutebell.wav"&gt;Lou Harrison - Concerto for Flute and Percussion Movement II&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;System Notification&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/flutebell.wav"&gt;Lou Harrison - Concerto for Flute and Percussion Movement II&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Exclamation&lt;/td&gt;&lt;td&gt;&lt;a href="http://neednom.com/bfisher/blog/systemsounds/alert.wav"&gt;Toru Takemitsu - November Steps for Orchestra&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;/table&gt;
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!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7285111147597217754?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7285111147597217754/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7285111147597217754' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7285111147597217754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7285111147597217754'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/system-sounds.html' title='System Sounds!'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-7324498589924148861</id><published>2007-08-10T12:00:00.001-07:00</published><updated>2010-08-31T22:56:58.602-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Speak'/><title type='text'>Chat History Reader</title><content type='html'>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.&lt;br/&gt;

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.&lt;br/&gt;

 &lt;a href="http://bp1.blogger.com/_sIga-_J94mI/Rry19J77LaI/AAAAAAAAADU/Y-OZBn2oi3w/s1600-h/screenshot2.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_sIga-_J94mI/Rry19J77LaI/AAAAAAAAADU/Y-OZBn2oi3w/s320/screenshot2.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5097148940520467874" /&gt;&lt;/a&gt;
&lt;a href="http://bp0.blogger.com/_sIga-_J94mI/Rry14577LZI/AAAAAAAAADM/Y_Jm_iz2TRo/s1600-h/screenshot1.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_sIga-_J94mI/Rry14577LZI/AAAAAAAAADM/Y_Jm_iz2TRo/s320/screenshot1.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5097148867506023826" /&gt;&lt;/a&gt;

&lt;a href="http://neednom.com/bfisher/blog/chat/chathistory.zip"&gt;Source and binary&lt;/a&gt; (68 k).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-7324498589924148861?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/7324498589924148861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=7324498589924148861' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7324498589924148861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/7324498589924148861'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/chat-history-reader.html' title='Chat History Reader'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_sIga-_J94mI/Rry19J77LaI/AAAAAAAAADU/Y-OZBn2oi3w/s72-c/screenshot2.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-4253000422980653312</id><published>2007-08-10T09:45:00.000-07:00</published><updated>2010-08-31T22:57:45.371-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Think'/><title type='text'>Visual Studio Shortcuts</title><content type='html'>Visual Studio Shortcuts

Here are some of the Visual Studio shortcuts I use.
&lt;h2&gt;Commands&lt;/h2&gt;
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 &gt;. 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.&lt;br/&gt;
There is a "command window" that is dedicated to this. I've mapped Ctrl+Space to View.CommandWindow, which is quicker than Ctrl+/, &gt;. You can set up an alias to any command. For example, to set up an alias to a commonly used file, enter this:
&lt;pre&gt;
alias m File.OpenFile CommonlyUsedFile.cs
&lt;/pre&gt;
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 &lt;i&gt;m&lt;/i&gt; /delete". I deleted most of the other aliases, as I'd rather type Ctrl+F4 then "close", and so on.&lt;br/&gt;
Useful aliases:
&lt;pre&gt;
sh Tools.Shell
o File.OpenFile
cls Edit.ClearAll
g Debug.Start
bl Debug.Breakpoints
kb Debug.ListCallStack
&lt;/pre&gt;
&lt;h2&gt;Macros&lt;/h2&gt;
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.
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;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(&amp;quot;(&amp;quot;, Mid$(line, i, 1), vbTextCompare) = 0 Then
  nparens = nparens + 1
    ElseIf StrComp(&amp;quot;)&amp;quot;, Mid$(line, i, 1), vbTextCompare) = 0 Then
  nparens = nparens - 1
    End If
Next

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

DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.Text = strResult.ToString()
DTE.ActiveDocument.Selection.NewLine()
End Sub
&lt;/code&gt;&lt;/pre&gt;
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.
&lt;h2&gt;Snippits&lt;/h2&gt;
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 &lt;a href="http://neednom.com/bfisher/blog/snippy/SnippySetup.zip"&gt;here&lt;/a&gt; (378k).&lt;br/&gt;
I wrote snippits for creating unit tests, switch statements, and some XAML structures. Most often I used prop and propd for quickly creating properties.
&lt;h2&gt;Shortcuts&lt;/h2&gt;
Finally, here are some keyboard bindings that I find useful:
&lt;table&gt;
 &lt;tr&gt;
  &lt;td&gt;Ctrl+Space&lt;/td&gt;
  &lt;td&gt;View.CommandWindow&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
  &lt;td&gt;Ctrl+Q&lt;/td&gt;
  &lt;td&gt;Edit.CommentSelection&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
  &lt;td&gt;Ctrl+Shift+Q&lt;/td&gt;
  &lt;td&gt;Edit.UncommentSelection&lt;/td&gt;
 &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Alt+,&lt;/td&gt;
   &lt;td&gt;Toggle Bookmark&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+,&lt;/td&gt;
   &lt;td&gt;Next Bookmark&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Shift+,&lt;/td&gt;
   &lt;td&gt;Prev Bookmark&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Alt+W&lt;/td&gt;
   &lt;td&gt;Watch Window&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Alt+K&lt;/td&gt;
   &lt;td&gt;Call stack&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Alt+R&lt;/td&gt;
   &lt;td&gt;Toggle Word wrap&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Shift+Alt+W&lt;/td&gt;
   &lt;td&gt;File.CloseSolution&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
If you happen to have MbUnit (which is cool) or VisualSVN,
&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+T&lt;/td&gt;
   &lt;td&gt;MbUnit – Run Tests&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Shift+R&lt;/td&gt;
   &lt;td&gt;VisualSVN-Revert Changes current item&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Shift+H&lt;/td&gt;
   &lt;td&gt;VisualSVN- Show current file changes&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Ctrl+Shift+Alt+H&lt;/td&gt;
   &lt;td&gt;VisualSVN – Show All Changes&lt;/td&gt;
  &lt;/tr&gt;
 &lt;/table&gt;
 &lt;p&gt;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 &lt;a href="http://msdn2.microsoft.com/en-us/library/Aa301782(VS.71).aspx"&gt;Clipboard ring&lt;/a&gt; is nice also.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-4253000422980653312?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/4253000422980653312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=4253000422980653312' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4253000422980653312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/4253000422980653312'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/visual-studio-shortcuts.html' title='Visual Studio Shortcuts'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7451330209409602076.post-5824790741468995851</id><published>2007-08-07T23:21:00.000-07:00</published><updated>2008-03-08T14:48:36.405-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='See'/><title type='text'>Adding Links to Web Album Generator</title><content type='html'>&lt;a href="http://www.ornj.net/"&gt;Web Album Generator&lt;/a&gt; is a great freeware program for creating online photo albums. It is sleek, easy to use, and does things the right way.&lt;br/&gt;
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.
&lt;br/&gt;
Here's a Python script that will do the trick. If you haven't used &lt;a href="http://www.ornj.net/"&gt;Web Album Generator&lt;/a&gt;, 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.)
&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;# Designed for Web Album Generator 1.8.2 (ornj.net)
import os
import re

strDirectory = r&amp;quot;C:\outputdirectory&amp;quot;
astrFiles = os.listdir(strDirectory)

reNextPicture = re.compile('&amp;lt;a href=&amp;quot;([^&amp;quot;]+.html)&amp;quot; title=&amp;quot;Next Photograph&amp;quot;&amp;gt;Next')
reEndlink = re.compile('(&amp;lt;/div&amp;gt;)\s+(&amp;lt;p class=&amp;quot;)')

def main():
  for strFilename in astrFiles:
    if strFilename.endswith(&amp;quot;html&amp;quot;):
      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('&amp;lt;div id=&amp;quot;photograph&amp;quot;&amp;gt;', '&amp;lt;div id=&amp;quot;photograph&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;'+filenext + '&amp;quot;&amp;gt;',1)
        strHtml = reEndlink.sub('&amp;lt;/a&amp;gt; &amp;lt;/div&amp;gt; &amp;lt;p class=&amp;quot;',strHtml)

        fout = open(strDirectory + '\\' + strFilename, 'w')
        fout.write(html)
        fout.close()
        print &amp;quot;Written:&amp;quot; + strFilename
main()
&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7451330209409602076-5824790741468995851?l=halfhourhacks.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://halfhourhacks.blogspot.com/feeds/5824790741468995851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7451330209409602076&amp;postID=5824790741468995851' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5824790741468995851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7451330209409602076/posts/default/5824790741468995851'/><link rel='alternate' type='text/html' href='http://halfhourhacks.blogspot.com/2007/08/adding-links-to-web-album-generator.html' title='Adding Links to Web Album Generator'/><author><name>ben.fisher</name><uri>http://www.blogger.com/profile/07306557553230548957</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
