<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Zach Denton</title>
    <link href="https://zach.se/rss.xml" rel="self" />
    <link href="https://zach.se" />
    <id>https://zach.se/rss.xml</id>
    <author>
        <name>Zach Denton</name>
        
        <email>z@chdenton.com</email>
        
    </author>
    <updated>2012-05-14T00:00:00Z</updated>
    <entry>
    <title>Export Facebook Contacts with Android</title>
    <link href="https://zach.se/export-facebook-contacts-with-android/" />
    <id>https://zach.se/export-facebook-contacts-with-android/</id>
    <published>2012-05-14T00:00:00Z</published>
    <updated>2012-05-14T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>Export Facebook Contacts with Android - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="How to use a rooted Android phone to export your Facebook address book.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">Export Facebook Contacts with Android</a>
</h1>
<section class="page-contents">
    <article>
        <p>If you have a rooted Android phone, you have full control over your
device — and unrestricted access to the data it contains. In this post,
I’m going to show you how you can use your rooted Android device to do
something that <a href="http://techcrunch.com/2011/02/22/google-android-facebook-contacts/">the average person can’t
do</a>:
namely, export your contacts from the Facebook app on Android.</p>
<h2 id="get-the-contacts-database">1. Get the contacts database</h2>
<p>It turns out that the Facebook app stores your contacts in an SQLite
database located on the filesystem at
<code>/data/data/com.facebook.katana/databases/fb.db</code>. In order to use this
database, we need to copy it to a computer. Since Android doesn’t
provide access to the internal storage via USB, I first copied the
database to my SD card using a root file browser, and then copied it to
my computer via USB.</p>
<h2 id="examine-the-contacts-database">2. Examine the contacts database</h2>
<p>Now that you have the database on your local machine, you can analyze it
and determine its structure. Begin by opening it up with the SQLite
client:</p>
<pre><code>$ sqlite3 /path/to/fb.db</code></pre>
<p>Now it’s just the standard procedure when dealing with an unknown
database:</p>
<h3 id="list-the-tables">2.1 List the tables</h3>
<pre><code>sqlite&gt; .tables
albums                    friends                   page_search_results
android_metadata          friends_data              perf_sessions
cache                     key_value                 photos
chatconversations         mailbox_messages          search_results
chatmessages              mailbox_messages_display  stream_photos
connections               mailbox_profiles          user_statuses
default_page_images       mailbox_threads           user_values
events                    notifications</code></pre>
<h3 id="determine-which-tables-contain-the-data-youre-looking-for">2.2 Determine which tables contain the data you’re looking for</h3>
<p>The table’s name is often a good indicator of its contents. In this
case, the <code>friends</code> table (technically, it’s a
<a href="http://www.sqlite.org/lang_createview.html">view</a>) contains all the
data we need.</p>
<h3 id="determine-which-columns-to-extract">2.3 Determine which columns to extract</h3>
<p>Here’s the structure of the <code>friends</code> view:</p>
<pre><code>sqlite&gt; .schema friends
CREATE VIEW friends AS SELECT connections._id AS _id, connections.user_id AS user_id, connections.display_name AS display_name, connections.connection_type AS connection_type, connections.user_image_url AS user_image_url, connections.user_image AS user_image, connections.hash AS hash, friends_data.first_name AS first_name, friends_data.last_name AS last_name, friends_data.cell AS cell, friends_data.other AS other, friends_data.email AS email, friends_data.birthday_month AS birthday_month, friends_data.birthday_day AS birthday_day, friends_data.birthday_year AS birthday_year FROM connections LEFT OUTER JOIN friends_data ON connections.user_id=friends_data.user_id WHERE connections.connection_type=0;</code></pre>
<p>In this case, the important columns are:</p>
<ul>
<li><code>display_name</code></li>
<li><code>user_image_url</code></li>
<li><code>first_name</code></li>
<li><code>last_name</code></li>
<li><code>cell</code></li>
<li><code>other</code></li>
<li><code>email</code></li>
<li><code>birthday_month</code></li>
<li><code>birthday_day</code></li>
<li><code>birthday_year</code></li>
</ul>
<p>At this point, the hard part is over and it’s just a matter of
extracting the data from the database.</p>
<h2 id="export-the-data">3. Export the data</h2>
<h3 id="the-vcard-format">3.1 The vCard format</h3>
<p>Now that we know exactly which data we need, we can export it from the
database into a useful format. The most useful format for contact data
appears to be <a href="https://en.wikipedia.org/wiki/VCard">vCard</a>, which is
almost universally supported in communication apps. I won’t go into the
details of the <a href="https://tools.ietf.org/html/rfc6350">vCard
specification</a>, but here’s an
example of what we’re trying to generate:</p>
<pre><code>BEGIN:VCARD
VERSION:3.0
N:Feynman;Richard;;;
FN:Richard Feynman
TEL;TYPE=HOME:+72973525698
EMAIL;TYPE=PREF:rfeynman@princeton.edu
BDAY:1918-5-11
REV:2012-01-08T18:36:35.814661
END:VCARD

BEGIN:VCARD
VERSION:3.0
N:Planck;Max;;;
FN:Max Planck
TEL;TYPE=CELL:+66260695729
EMAIL;TYPE=PREF:planck@berlin.de
BDAY:1858-4-23
REV:2012-01-08T18:36:36.043204
END:VCARD</code></pre>
<p>Notice that multiple contacts can exist in the same file.</p>
<h3 id="a-script-to-generate-the-vcard">3.2 A script to generate the vCard</h3>
<p>I’ve written a Python script to generate a vCard from the <code>fb.db</code>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> base64</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> codecs</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sqlite3</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> urllib2</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> argparse</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> datetime</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> parse_database(database):</span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    conn <span class="op">=</span> sqlite3.<span class="ex">connect</span>(database)</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>    conn.row_factory<span class="op">=</span>sqlite3.Row</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>    c <span class="op">=</span> conn.cursor()</span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>    fields <span class="op">=</span> <span class="st">'display_name, user_image_url, first_name, '</span> <span class="op">+\</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>             <span class="st">'last_name, cell, other, email, birthday_month, '</span> <span class="op">+\</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>             <span class="st">'birthday_day, birthday_year'</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>    c.execute(<span class="st">'select </span><span class="sc">%s</span><span class="st"> from friends'</span> <span class="op">%</span> fields)</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> [row <span class="cf">for</span> row <span class="kw">in</span> c]</span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> generate_vcard(contact, photos<span class="op">=</span><span class="va">False</span>):</span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>    card <span class="op">=</span> <span class="st">&quot;BEGIN:VCARD</span><span class="ch">\n</span><span class="st">VERSION:3.0</span><span class="ch">\n</span><span class="st">&quot;</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>    card <span class="op">+=</span> <span class="st">&quot;N:</span><span class="sc">%s</span><span class="st">;</span><span class="sc">%s</span><span class="st">;;;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">%</span> (contact[<span class="st">'last_name'</span>], contact[<span class="st">'first_name'</span>])</span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a>    card <span class="op">+=</span> <span class="st">&quot;FN:</span><span class="sc">%s</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">%</span> contact[<span class="st">'display_name'</span>]</span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> contact[<span class="st">'cell'</span>]:</span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a>        card <span class="op">+=</span> <span class="st">'TEL;TYPE=CELL:</span><span class="sc">%s</span><span class="ch">\n</span><span class="st">'</span> <span class="op">%</span> contact[<span class="st">'cell'</span>]</span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> contact[<span class="st">'other'</span>]:</span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a>        card <span class="op">+=</span> <span class="st">'TEL;TYPE=HOME:</span><span class="sc">%s</span><span class="ch">\n</span><span class="st">'</span> <span class="op">%</span> contact[<span class="st">'other'</span>]</span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> contact[<span class="st">'email'</span>]:</span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a>        card <span class="op">+=</span> <span class="st">&quot;EMAIL;TYPE=PREF:</span><span class="sc">%s</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">%</span> contact[<span class="st">'email'</span>]</span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a>    birthday <span class="op">=</span> <span class="st">&quot;-&quot;</span>.join([<span class="bu">str</span>(f) <span class="cf">for</span> f <span class="kw">in</span> [contact[<span class="st">'birthday_year'</span>],</span>
<span id="cb5-35"><a href="#cb5-35" aria-hidden="true" tabindex="-1"></a>                                          contact[<span class="st">'birthday_month'</span>],</span>
<span id="cb5-36"><a href="#cb5-36" aria-hidden="true" tabindex="-1"></a>                                          contact[<span class="st">'birthday_day'</span>]]</span>
<span id="cb5-37"><a href="#cb5-37" aria-hidden="true" tabindex="-1"></a>                         <span class="cf">if</span> f <span class="op">!=</span> <span class="op">-</span><span class="dv">1</span>])</span>
<span id="cb5-38"><a href="#cb5-38" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> birthday:</span>
<span id="cb5-39"><a href="#cb5-39" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> birthday.count(<span class="st">'-'</span>) <span class="op">==</span> <span class="dv">1</span>: birthday <span class="op">=</span> <span class="st">&quot;1900-&quot;</span> <span class="op">+</span> birthday <span class="co"># default year</span></span>
<span id="cb5-40"><a href="#cb5-40" aria-hidden="true" tabindex="-1"></a>        card <span class="op">+=</span> <span class="st">&quot;BDAY:</span><span class="sc">%s</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">%</span> birthday</span>
<span id="cb5-41"><a href="#cb5-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-42"><a href="#cb5-42" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> photos <span class="kw">and</span> contact[<span class="st">'user_image_url'</span>]:</span>
<span id="cb5-43"><a href="#cb5-43" aria-hidden="true" tabindex="-1"></a>        <span class="cf">try</span>:</span>
<span id="cb5-44"><a href="#cb5-44" aria-hidden="true" tabindex="-1"></a>            photo <span class="op">=</span> urllib2.urlopen(contact[<span class="st">'user_image_url'</span>]).read()</span>
<span id="cb5-45"><a href="#cb5-45" aria-hidden="true" tabindex="-1"></a>            card <span class="op">+=</span> <span class="st">&quot;PHOTO;ENCODING=B;TYPE=JPEG:</span><span class="sc">%s</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">%\</span></span>
<span id="cb5-46"><a href="#cb5-46" aria-hidden="true" tabindex="-1"></a>                    base64.b64encode(photo)</span>
<span id="cb5-47"><a href="#cb5-47" aria-hidden="true" tabindex="-1"></a>        <span class="cf">except</span>:</span>
<span id="cb5-48"><a href="#cb5-48" aria-hidden="true" tabindex="-1"></a>            <span class="cf">pass</span></span>
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-50"><a href="#cb5-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true" tabindex="-1"></a>    card <span class="op">+=</span> <span class="st">&quot;REV:</span><span class="sc">%s</span><span class="ch">\n</span><span class="st">END:VCARD</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">%</span> datetime.datetime.now().isoformat()</span>
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> card</span>
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> main():</span>
<span id="cb5-55"><a href="#cb5-55" aria-hidden="true" tabindex="-1"></a>    parser <span class="op">=</span> argparse.ArgumentParser()</span>
<span id="cb5-56"><a href="#cb5-56" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'database'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;path to facebook database&quot;</span>, default<span class="op">=</span><span class="st">&quot;fb.db&quot;</span>, nargs<span class="op">=</span><span class="st">'?'</span>)</span>
<span id="cb5-57"><a href="#cb5-57" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'vcard'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;file to write contacts to&quot;</span>, default<span class="op">=</span><span class="st">&quot;fbcontacts.vcf&quot;</span>, nargs<span class="op">=</span><span class="st">'?'</span>)</span>
<span id="cb5-58"><a href="#cb5-58" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'--photos'</span>, action<span class="op">=</span><span class="st">&quot;store_true&quot;</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;download profile pictures&quot;</span>)</span>
<span id="cb5-59"><a href="#cb5-59" aria-hidden="true" tabindex="-1"></a>    args <span class="op">=</span> parser.parse_args()</span>
<span id="cb5-60"><a href="#cb5-60" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-61"><a href="#cb5-61" aria-hidden="true" tabindex="-1"></a>    <span class="cf">with</span> codecs.<span class="bu">open</span>(args.vcard, <span class="st">'w'</span>, <span class="st">'utf-8'</span>) <span class="im">as</span> vcard:</span>
<span id="cb5-62"><a href="#cb5-62" aria-hidden="true" tabindex="-1"></a>        contacts <span class="op">=</span> parse_database(args.database)</span>
<span id="cb5-63"><a href="#cb5-63" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> i, contact <span class="kw">in</span> <span class="bu">enumerate</span>(contacts):</span>
<span id="cb5-64"><a href="#cb5-64" aria-hidden="true" tabindex="-1"></a>            sys.stderr.write(<span class="st">&quot;</span><span class="ch">\r</span><span class="st">exporting contact </span><span class="sc">%s</span><span class="st"> of </span><span class="sc">%s</span><span class="st">&quot;</span> <span class="op">%</span> (i<span class="op">+</span><span class="dv">1</span>, <span class="bu">len</span>(contacts)))</span>
<span id="cb5-65"><a href="#cb5-65" aria-hidden="true" tabindex="-1"></a>            card <span class="op">=</span> generate_vcard(contact, args.photos)</span>
<span id="cb5-66"><a href="#cb5-66" aria-hidden="true" tabindex="-1"></a>            <span class="bu">print</span> <span class="op">&gt;&gt;</span> vcard, card</span>
<span id="cb5-67"><a href="#cb5-67" aria-hidden="true" tabindex="-1"></a>            sys.stderr.flush()</span>
<span id="cb5-68"><a href="#cb5-68" aria-hidden="true" tabindex="-1"></a>        sys.stderr.write(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb5-69"><a href="#cb5-69" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-70"><a href="#cb5-70" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>: main()</span></code></pre></div>
<p>It just goes through each row, grabs the relevant data, and writes it
out in vCard format.</p>
<p>To use the script, just run <code>python fbcontacts.py /path/to/fb.db</code>.
You’ll end up with all of your contacts in a vCard file, complete with
email addresses, phone numbers, birthdays, and (if you specify the
<code>--photos</code> flag) profile pictures.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Restrictions don’t apply to those with rooted phones and a bit of
curiosity.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>Music Server Revisited: Streaming with sshfs and mp3fs</title>
    <link href="https://zach.se/music-server-revisited-streaming-with-sshfs-and-mp3fs/" />
    <id>https://zach.se/music-server-revisited-streaming-with-sshfs-and-mp3fs/</id>
    <published>2011-12-29T00:00:00Z</published>
    <updated>2011-12-29T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>Music Server Revisited: Streaming with sshfs and mp3fs - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="Access a remote music collection seamlessly with user-space filesystems.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">Music Server Revisited: Streaming with sshfs and mp3fs</a>
</h1>
<section class="page-contents">
    <article>
        <p>A while back, I wrote about <a href="../build-a-media-server-with-ampache-and-transmission/">streaming music with
Ampache</a>. Ampache
is a nice piece of software, with some unique advantages (like Android
support). However, there are simpler ways to stream music. I’m going to
show you how to stream music using sshfs and (optionally) mp3fs. One of
the best things about this approach is that it allows you to access your
collection with the media player of your choice.</p>
<p>Before you start, you’re going to need a Linux machine with your music
collection on it (the server). You will also need another Linux machine
to act as the client (e.g. a laptop with a small hard drive).</p>
<h2 id="configuring-the-server">Configuring the server</h2>
<h3 id="install-the-ssh-server">Install the ssh server</h3>
<p>The first thing you’re going to want to do is install an ssh server, if
it’s not already installed.</p>
<pre><code>$ sudo aptitude install openssh-server</code></pre>
<p>If your music is already compressed, or if you just have a very fast
internet connection, you’re done configuring the server. Otherwise, read
on to learn how to transcode your music on-the-fly with mp3fs.</p>
<h3 id="set-up-mp3fs">Set up mp3fs</h3>
<blockquote>
<p>MP3FS is a read-only FUSE filesystem which transcodes audio formats
(currently FLAC) to MP3 on the fly when opened and read. This was
written to enable me to use my FLAC collection with software and/or
hardware which only understands the MP3 format e.g. gmediaserver to a
Netgear MP101 MP3 player. — <a href="http://khenriks.github.com/mp3fs/">Kristofer
Henriksson</a></p>
</blockquote>
<p>If you have any music in FLAC format, you’re probably not going to be
able to stream it without first transcoding to MP3. mp3fs solves this
problem. First, install it.</p>
<pre><code>$ sudo aptitude install mp3fs</code></pre>
<p>NOTE: Depending on your distro, the repositories might have an outdated
version of mp3fs. If the following commands don’t work, try installing a
newer version of mp3fs (either by compiling it yourself or finding a
newer package).</p>
<p>Now that it’s installed, we’ll set it up. Create a folder for the mp3fs:</p>
<pre><code>$ mkdir ~/MP3</code></pre>
<p>Next, add an entry to <code>/etc/fstab</code> to automatically mount the mp3fs:</p>
<pre><code># cat &gt;&gt; /etc/fstab &lt;&lt; EOF
mp3fs#/home/USER/Music /home/USER/MP3 fuse allow_other,ro,bitrate=320 0 0
EOF</code></pre>
<p>Make sure you adjust the paths and bitrate, if necessary.</p>
<p>Finally, mount the mp3fs:</p>
<pre><code>$ sudo mount -a</code></pre>
<p>Now your music collection should appear at <code>~/MP3</code> in MP3 format.</p>
<h2 id="configuring-the-client">Configuring the client</h2>
<p>We’re going to use sshfs to stream the music. It uses SSH to allow you
to mount remote filesystems on the local machine. In other words, it
will make your remote music collection available on the client machine.
To start, install sshfs.</p>
<pre><code>$ sudo aptitude install sshfs</code></pre>
<p>Now, mount the remote music collection (adjust the parameters as
necessary):</p>
<pre><code>$ sshfs USER@example.com:Music ~/Music</code></pre>
<p>Your music collection should now be visible on the client at <code>~/Music</code>.
Fire up your favorite media player, allow it to scan your music library,
and enjoy.</p>
<p>If you want the sshfs to be automatically mounted, you will first need
to <a href="http://www.ece.uci.edu/~chou/ssh-key.html">set up key-based SSH
authentication</a>. Once that’s
done, you will need to edit <code>/etc/fstab</code>:</p>
<pre><code># cat &gt;&gt; /etc/fstab &lt;&lt; EOF
sshfs#USER@example.com:Music /home/USER/Music fuse defaults,idmap=user 0 0
EOF</code></pre>
<p>Now your music collection will be available at login.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>A* Search with Multiple Targets and Sources</title>
    <link href="https://zach.se/a-star-search-with-multiple-targets-and-sources/" />
    <id>https://zach.se/a-star-search-with-multiple-targets-and-sources/</id>
    <published>2011-12-29T00:00:00Z</published>
    <updated>2011-12-29T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>A* Search with Multiple Targets and Sources - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="An extension of the A* algorithm to support pathfinding from many starting points to many goal points in a single pass.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">A* Search with Multiple Targets and Sources</a>
</h1>
<section class="page-contents">
    <article>
        <p>A couple months ago, I decided to expand my knowledge of artificial
intelligence by participating in the <a href="http://aichallenge.org">2011 AI
Challenge</a>. It was an incredibly interesting and
educational experience. Sadly, I was forced to divert my attention away
from the AI Challenge to focus on more important things.</p>
<p>One of the more interesting things I learned from the AI Challenge was
an A* algorithm which supports searching from multiple sources to
multiple targets, simultaneously. I used this algorithm to efficiently
gather food: one pass of this algorithm calculated paths to each food
tile.</p>
<p>The algorithm is similar to the basic A*, except for a few key
differences. First of all, the frontier is prepopulated with all of the
sources. Secondly, the goal test function checks to see if the current
position corresponds to <em>any</em> of the targets, not just a single target.
Finally, the heuristic is the Manhattan distance to the <em>nearest</em>
target.</p>
<p>This is the algorithm in pseudocode:</p>
<pre><code>frontier = {sources}
explored = {}
loop:
    if frontier is empty: return
    path = remove_choice(frontier) # minimize f + h where h = distance to nearest target
    s = path.end
    add s to explored
    if s in targets: command source to follow path
    for a in actions:
        add [path + a -&gt; Result(s, a)] to frontier
            unless Result(s, a) in frontier + explored</code></pre>
<p>I’ve <a href="https://github.com/zacharydenton/aichallenge-ants">uploaded my bot to
GitHub</a>, so you can
actually <a href="https://github.com/zacharydenton/aichallenge-ants/blob/master/MyBot.java#L237">see this algorithm in
practice</a>
if you want.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>Generate Audio with Python</title>
    <link href="https://zach.se/generate-audio-with-python/" />
    <id>https://zach.se/generate-audio-with-python/</id>
    <published>2011-04-23T00:00:00Z</published>
    <updated>2011-04-23T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>Generate Audio with Python - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="An introduction to audio synthesis in Python using itertools.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">Generate Audio with Python</a>
</h1>
<section class="page-contents">
    <article>
        <h2 id="introduction">Introduction</h2>
<p>I’ve been intrigued by the concept of using computers to generate audio
for a long time. It turns out that you can generate audio with nothing
but the standard library of Python.</p>
<p>The approach I used relies heavily on the <code>itertools</code> module.
Essentially, I use <code>itertools</code> to create infinite generators and then
take some data from these generators to produce the audio. The
resultant sequence of floats in the range [-1.0, 1.0] is converted to 16 bit
PCM audio (i.e., a sequence of signed 16 bit integers in the range
[-32767, 32767]) and then written to a .wav file using the <code>wave</code>
module.
<!--more--></p>
<p>If you’re not familiar with iterators and the <code>itertools</code> module, this
post may be somewhat hard to follow. <code>itertools</code> really opens up some
interesting possibilities in Python, making it more like Lisp or
Haskell. In truth, if you’re relying on <code>itertools</code> as much as I am in
this post, you might as well just use Lisp or Haskell and receive a nice
performance boost. The reason I didn’t is quite simply
because I wanted to generate some audio in Python.</p>
<p>To follow the code examples below, you probably need to perform the following imports:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> wave</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> math</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> struct</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> random</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> argparse</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> itertools <span class="im">import</span> <span class="op">*</span></span></code></pre></div>
<h2 id="generating-waves">Generating Waves</h2>
<p>You may remember from your physics class that sound consists of waves.
Many instruments produce tones that are basically a combination of pure
sine waves. Thus, we need a way to produce sine waves if we want to
generate audio. My first approach was something like this:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> sine_wave(frequency<span class="op">=</span><span class="fl">440.0</span>, framerate<span class="op">=</span><span class="dv">44100</span>, amplitude<span class="op">=</span><span class="fl">0.5</span>):</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&gt;</span> <span class="fl">1.0</span>: amplitude <span class="op">=</span> <span class="fl">1.0</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&lt;</span> <span class="fl">0.0</span>: amplitude <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> (<span class="bu">float</span>(amplitude) <span class="op">*</span> math.sin(<span class="fl">2.0</span><span class="op">*</span>math.pi<span class="op">*</span><span class="bu">float</span>(frequency)<span class="op">*</span>(<span class="bu">float</span>(i)<span class="op">/</span><span class="bu">float</span>(framerate))) <span class="cf">for</span> i <span class="kw">in</span> count(<span class="dv">0</span>))</span></code></pre></div>
<p>This computes a sine wave of infinite length at the specified frequency,
and returns an infinite generator which samples the wave 44,100 times
per second.</p>
<p>The problem with this approach is that it is inefficient. Sine waves are
periodic functions, meaning that they repeat themselves after a certain
period. This means that we can pre-calculate the function for one
period, and then return an iterator which simply cycles these
pre-computed values indefinitely:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> sine_wave(frequency<span class="op">=</span><span class="fl">440.0</span>, framerate<span class="op">=</span><span class="dv">44100</span>, amplitude<span class="op">=</span><span class="fl">0.5</span>):</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    period <span class="op">=</span> <span class="bu">int</span>(framerate <span class="op">/</span> frequency)</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&gt;</span> <span class="fl">1.0</span>: amplitude <span class="op">=</span> <span class="fl">1.0</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&lt;</span> <span class="fl">0.0</span>: amplitude <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    lookup_table <span class="op">=</span> [<span class="bu">float</span>(amplitude) <span class="op">*</span> math.sin(<span class="fl">2.0</span><span class="op">*</span>math.pi<span class="op">*</span><span class="bu">float</span>(frequency)<span class="op">*</span>(<span class="bu">float</span>(i<span class="op">%</span>period)<span class="op">/</span><span class="bu">float</span>(framerate))) <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">xrange</span>(period)]</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> (lookup_table[i<span class="op">%</span>period] <span class="cf">for</span> i <span class="kw">in</span> count(<span class="dv">0</span>))</span></code></pre></div>
<p>This resulted in a substantial performance improvement on my machine,
but this is Python after all so a discussion of performance is perhaps a moot
point.</p>
<h2 id="generating-noise">Generating Noise</h2>
<p>Sometimes you want to generate noise. The simplest kind of noise is
called white noise, which is completely random audio data.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> white_noise(amplitude<span class="op">=</span><span class="fl">0.5</span>):</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> (<span class="bu">float</span>(amplitude) <span class="op">*</span> random.uniform(<span class="op">-</span><span class="dv">1</span>, <span class="dv">1</span>) <span class="cf">for</span> _ <span class="kw">in</span> count(<span class="dv">0</span>))</span></code></pre></div>
<p>The main downside to this approach is that random values need to be calculated
44,100 times per second. Using the <code>itertools</code> module, we can
pre-calculate one second of white noise, and then just cycle that data:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>noise <span class="op">=</span> cycle(islice(white_noise(), <span class="dv">44100</span>))</span></code></pre></div>
<h2 id="combining-functions">Combining Functions</h2>
<p>As I mentioned earlier, complex sounds can be modeled as combinations of
pure sine waves. If you’re generating a stereo audio file, you can have
different audio functions in each channel. The way I chose to represent
this concept is as follows:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>c1 <span class="op">=</span> (f1, ..., fn)</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>c2 <span class="op">=</span> (f1, ..., fn)</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>channels <span class="op">=</span> (c1, c2)</span></code></pre></div>
<p><code>c1</code> is the left channel, <code>c2</code> is the right channel. Each channel is an
iterable containing the functions that comprise that channel. All
channels are then combined into a single iterable, <code>channels</code>.</p>
<p>If you play the same sound through both channels of a stereo audio file,
the sound will seem to come from the center of the soundstage.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>channels <span class="op">=</span> ((sine_wave(<span class="fl">440.0</span>),),</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>            (sine_wave(<span class="fl">440.0</span>),))</span></code></pre></div>
<p>You can also control the location of the sound by altering the amplitude
of the waves. This example will make a 440.0 Hz sine wave which is
slightly left of center:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>channels <span class="op">=</span> ((sine_wave(<span class="fl">440.0</span>, amplitude<span class="op">=</span><span class="fl">0.5</span>),),</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>            (sine_wave(<span class="fl">440.0</span>, amplitude<span class="op">=</span><span class="fl">0.2</span>),))</span></code></pre></div>
<p>Additionally, you can have more than one function playing at the same
time. Here’s an example of a 200.0 Hz tone in the left channel, a 205.0
tone in the right channel, and some white noise in the background:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>channels <span class="op">=</span> ((sine_wave(<span class="fl">200.0</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>), white_noise(amplitude<span class="op">=</span><span class="fl">0.001</span>)),</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>            (sine_wave(<span class="fl">205.0</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>), white_noise(amplitude<span class="op">=</span><span class="fl">0.001</span>)))</span></code></pre></div>
<p>That’s a <a href="http://en.wikipedia.org/wiki/Binaural_beats">binaural beat</a>.</p>
<h2 id="computing-samples">Computing Samples</h2>
<p>Recall from your physics class that waves combine with each other to
produce new waves. We can compute this new wave by simply adding the
waves together.</p>
<p>Now that we have defined the audio channels, we need to compute the sum
of the functions in the channel at each sample in the file. Since our waves
are represented as generators, we want to create a new generator which
calculates the sum of each sample in the input generators. Essentially
we want a function that accepts audio channels in the format described
above and returns a generator which yields tuples where element 0 is the
sum of the functions in the left channel at that point, and element 1 is
the sum of the functions in the right channel at that point.</p>
<p>This calls for use of the <code>imap</code> and <code>izip</code> functions.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> compute_samples(channels, nsamples<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> islice(izip(<span class="op">*</span>(imap(<span class="bu">sum</span>, izip(<span class="op">*</span>channel)) <span class="cf">for</span> channel <span class="kw">in</span> channels)), nsamples)</span></code></pre></div>
<p>Note that if <code>nsamples</code> is specified, we return a sequence of finite
length (using the <code>islice</code> function). Otherwise, we return a sequence of
infinite length. Since we are using iterators, sequences of infinite
length can be represented more or less elegantly and efficiently.</p>
<h2 id="writing-a-wavefile">Writing a Wavefile</h2>
<p>The next step is to use the <code>wave</code> module to create a <code>.wav</code> file. The
first thing to do is to generate the wave header, which is some
information at the beginning of the wavefile that describes the contents
of the file. The information we need to generate this header is as
follows:</p>
<ul>
<li><code>nchannels</code> - the number of channels contained in the file. For stereo, this is 2.</li>
<li><code>sampwidth</code> - the size of each sample, in bytes. Recall that a byte is 8 bits, so for 16 bit audio this is 2.</li>
<li><code>framerate</code> - the number of samples per second. I usually set this to 44100 (CD quality).</li>
<li><code>nframes</code> - the total number of samples to write. This is equal to the framerate multiplied by the duration of the file in seconds.</li>
</ul>
<p>To open a wavefile for writing with the <code>wave</code> module, do this:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>w <span class="op">=</span> wave.<span class="bu">open</span>(filename, <span class="st">'w'</span>)</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>w.setparams((nchannels, sampwidth, framerate, nframes, <span class="st">'NONE'</span>, <span class="st">'not compressed'</span>))</span></code></pre></div>
<p>The <code>'NONE'</code> and <code>'not compressed'</code> just indicate that we are creating an
uncompressed wavefile (nothing else is supported by the <code>wave</code> module at
the time of writing).</p>
<p>Now the wavefile is ready for our audio data. 16 bit audio is encoded
as a series of signed 16 bit integers. The first thing to do is to scale
our sequence of floats in the range [-1.0, 1.0] to signed 16 bit
integers (in the range [-32767, 32767]). For example:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>max_amplitude <span class="op">=</span> <span class="fl">32767.0</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>samples <span class="op">=</span> (<span class="bu">int</span>(sample <span class="op">*</span> max_amplitude) <span class="cf">for</span> sample <span class="kw">in</span> samples)</span></code></pre></div>
<p>We’re writing a binary format, so we need the <code>struct</code> module
to convert our audio data to the correct binary encoding. Specifically,
we need the <code>struct.pack</code> function. The <code>struct.pack</code> function uses
format strings to designate how to pack the data. A signed 16 bit
integer is also known as a signed short, so we want to use the format
string ‘h’ (the format string for a signed short). Thus, to pack the
integer 1000 into a signed short:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>struct.pack(<span class="st">'h'</span>, <span class="dv">1000</span>)</span></code></pre></div>
<p>Now, we are going to be creating stereo audio files, so we need to
consider how <code>.wav</code> files represent multiple channels. It turns out
that <code>.wav</code> files look something like this:</p>
<pre><code>L1R1L2R2L3R3L4R4</code></pre>
<p>Where <code>L1</code> is the first sample in the left channel, <code>R1</code> is the first
sample in the right channel, and so on. In other words, the channels are
interleaved.</p>
<p>Finally, we want to keep performance in mind. On one extreme, we write
data to the file every time we compute a sample. This is
memory-efficient, but incurs a severe performance penalty due to the
overhead of writing to the file. On the other extreme, we pre-compute
the entire file and write all of the samples at once. This does not
incur the aforementioned performance penalty, but it has two major
problems. First, it requires a huge amount of memory, since the entire
<code>.wav</code> file will be loaded into memory. Second, it means you can’t
stream the audio as it is generated, which means you can’t play the
audio in realtime (by writing to <code>stdout</code> and piping to <code>aplay</code>, for
example).</p>
<p>Thus, we take the third approach: buffer chunks of the audio
stream and write each chunk as it is computed. This offers the
advantages of both techniques.</p>
<p>So, putting all of this together, we end up with something like this:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> grouper(n, iterable, fillvalue<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;grouper(3, 'ABCDEFG', 'x') --&gt; ABC DEF Gxx&quot;</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>    args <span class="op">=</span> [<span class="bu">iter</span>(iterable)] <span class="op">*</span> n</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> izip_longest(fillvalue<span class="op">=</span>fillvalue, <span class="op">*</span>args)</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> write_wavefile(filename, samples, nframes<span class="op">=</span><span class="va">None</span>, nchannels<span class="op">=</span><span class="dv">2</span>, sampwidth<span class="op">=</span><span class="dv">2</span>, framerate<span class="op">=</span><span class="dv">44100</span>, bufsize<span class="op">=</span><span class="dv">2048</span>):</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> nframes <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>        nframes <span class="op">=</span> <span class="op">-</span><span class="dv">1</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a>    w <span class="op">=</span> wave.<span class="bu">open</span>(filename, <span class="st">'w'</span>)</span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a>    w.setparams((nchannels, sampwidth, framerate, nframes, <span class="st">'NONE'</span>, <span class="st">'not compressed'</span>))</span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a>    max_amplitude <span class="op">=</span> <span class="bu">float</span>(<span class="bu">int</span>((<span class="dv">2</span> <span class="op">**</span> (sampwidth <span class="op">*</span> <span class="dv">8</span>)) <span class="op">/</span> <span class="dv">2</span>) <span class="op">-</span> <span class="dv">1</span>)</span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a>    <span class="co"># split the samples into chunks (to reduce memory consumption and improve performance)</span></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> chunk <span class="kw">in</span> grouper(bufsize, samples):</span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a>        frames <span class="op">=</span> <span class="st">''</span>.join(<span class="st">''</span>.join(struct.pack(<span class="st">'h'</span>, <span class="bu">int</span>(max_amplitude <span class="op">*</span> sample)) <span class="cf">for</span> sample <span class="kw">in</span> channels) <span class="cf">for</span> channels <span class="kw">in</span> chunk <span class="cf">if</span> channels <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>)</span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true" tabindex="-1"></a>        w.writeframesraw(frames)</span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a>    w.close()</span>
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> filename</span></code></pre></div>
<h2 id="wavebender"><code>wavebender</code></h2>
<p>I have compiled these techniques and a few others into a module
called <code>wavebender</code>. Here’s the current source code (at the time
of writing), but you can always find the latest
at <a href="https://github.com/zacharydenton/wavebender" class="uri">https://github.com/zacharydenton/wavebender</a>.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> wave</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> math</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> struct</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> random</span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> argparse</span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> itertools <span class="im">import</span> <span class="op">*</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> grouper(n, iterable, fillvalue<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;grouper(3, 'ABCDEFG', 'x') --&gt; ABC DEF Gxx&quot;</span></span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a>    args <span class="op">=</span> [<span class="bu">iter</span>(iterable)] <span class="op">*</span> n</span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> izip_longest(fillvalue<span class="op">=</span>fillvalue, <span class="op">*</span>args)</span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> sine_wave(frequency<span class="op">=</span><span class="fl">440.0</span>, framerate<span class="op">=</span><span class="dv">44100</span>, amplitude<span class="op">=</span><span class="fl">0.5</span>):</span>
<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a>    <span class="co">'''</span></span>
<span id="cb16-17"><a href="#cb16-17" aria-hidden="true" tabindex="-1"></a><span class="co">    Generate a sine wave at a given frequency of infinite length.</span></span>
<span id="cb16-18"><a href="#cb16-18" aria-hidden="true" tabindex="-1"></a><span class="co">    '''</span></span>
<span id="cb16-19"><a href="#cb16-19" aria-hidden="true" tabindex="-1"></a>    period <span class="op">=</span> <span class="bu">int</span>(framerate <span class="op">/</span> frequency)</span>
<span id="cb16-20"><a href="#cb16-20" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&gt;</span> <span class="fl">1.0</span>: amplitude <span class="op">=</span> <span class="fl">1.0</span></span>
<span id="cb16-21"><a href="#cb16-21" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&lt;</span> <span class="fl">0.0</span>: amplitude <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb16-22"><a href="#cb16-22" aria-hidden="true" tabindex="-1"></a>    lookup_table <span class="op">=</span> [<span class="bu">float</span>(amplitude) <span class="op">*</span> math.sin(<span class="fl">2.0</span><span class="op">*</span>math.pi<span class="op">*</span><span class="bu">float</span>(frequency)<span class="op">*</span>(<span class="bu">float</span>(i<span class="op">%</span>period)<span class="op">/</span><span class="bu">float</span>(framerate))) <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">xrange</span>(period)]</span>
<span id="cb16-23"><a href="#cb16-23" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> (lookup_table[i<span class="op">%</span>period] <span class="cf">for</span> i <span class="kw">in</span> count(<span class="dv">0</span>))</span>
<span id="cb16-24"><a href="#cb16-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-25"><a href="#cb16-25" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> square_wave(frequency<span class="op">=</span><span class="fl">440.0</span>, framerate<span class="op">=</span><span class="dv">44100</span>, amplitude<span class="op">=</span><span class="fl">0.5</span>):</span>
<span id="cb16-26"><a href="#cb16-26" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> s <span class="kw">in</span> sine_wave(frequency, framerate, amplitude):</span>
<span id="cb16-27"><a href="#cb16-27" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> s <span class="op">&gt;</span> <span class="dv">0</span>:</span>
<span id="cb16-28"><a href="#cb16-28" aria-hidden="true" tabindex="-1"></a>            <span class="cf">yield</span> amplitude</span>
<span id="cb16-29"><a href="#cb16-29" aria-hidden="true" tabindex="-1"></a>        <span class="cf">elif</span> s <span class="op">&lt;</span> <span class="dv">0</span>:</span>
<span id="cb16-30"><a href="#cb16-30" aria-hidden="true" tabindex="-1"></a>            <span class="cf">yield</span> <span class="op">-</span>amplitude</span>
<span id="cb16-31"><a href="#cb16-31" aria-hidden="true" tabindex="-1"></a>        <span class="cf">else</span>:</span>
<span id="cb16-32"><a href="#cb16-32" aria-hidden="true" tabindex="-1"></a>            <span class="cf">yield</span> <span class="fl">0.0</span></span>
<span id="cb16-33"><a href="#cb16-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-34"><a href="#cb16-34" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> damped_wave(frequency<span class="op">=</span><span class="fl">440.0</span>, framerate<span class="op">=</span><span class="dv">44100</span>, amplitude<span class="op">=</span><span class="fl">0.5</span>, length<span class="op">=</span><span class="dv">44100</span>):</span>
<span id="cb16-35"><a href="#cb16-35" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&gt;</span> <span class="fl">1.0</span>: amplitude <span class="op">=</span> <span class="fl">1.0</span></span>
<span id="cb16-36"><a href="#cb16-36" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> amplitude <span class="op">&lt;</span> <span class="fl">0.0</span>: amplitude <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb16-37"><a href="#cb16-37" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> (math.exp(<span class="op">-</span>(<span class="bu">float</span>(i<span class="op">%</span>length)<span class="op">/</span><span class="bu">float</span>(framerate))) <span class="op">*</span> s <span class="cf">for</span> i, s <span class="kw">in</span> <span class="bu">enumerate</span>(sine_wave(frequency, framerate, amplitude)))</span>
<span id="cb16-38"><a href="#cb16-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-39"><a href="#cb16-39" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> white_noise(amplitude<span class="op">=</span><span class="fl">0.5</span>):</span>
<span id="cb16-40"><a href="#cb16-40" aria-hidden="true" tabindex="-1"></a>    <span class="co">'''</span></span>
<span id="cb16-41"><a href="#cb16-41" aria-hidden="true" tabindex="-1"></a><span class="co">    Generate random samples.</span></span>
<span id="cb16-42"><a href="#cb16-42" aria-hidden="true" tabindex="-1"></a><span class="co">    '''</span></span>
<span id="cb16-43"><a href="#cb16-43" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> (<span class="bu">float</span>(amplitude) <span class="op">*</span> random.uniform(<span class="op">-</span><span class="dv">1</span>, <span class="dv">1</span>) <span class="cf">for</span> i <span class="kw">in</span> count(<span class="dv">0</span>))</span>
<span id="cb16-44"><a href="#cb16-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-45"><a href="#cb16-45" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> compute_samples(channels, nsamples<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb16-46"><a href="#cb16-46" aria-hidden="true" tabindex="-1"></a>    <span class="co">'''</span></span>
<span id="cb16-47"><a href="#cb16-47" aria-hidden="true" tabindex="-1"></a><span class="co">    create a generator which computes the samples.</span></span>
<span id="cb16-48"><a href="#cb16-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-49"><a href="#cb16-49" aria-hidden="true" tabindex="-1"></a><span class="co">    essentially it creates a sequence of the sum of each function in the channel</span></span>
<span id="cb16-50"><a href="#cb16-50" aria-hidden="true" tabindex="-1"></a><span class="co">    at each sample in the file for each channel.</span></span>
<span id="cb16-51"><a href="#cb16-51" aria-hidden="true" tabindex="-1"></a><span class="co">    '''</span></span>
<span id="cb16-52"><a href="#cb16-52" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> islice(izip(<span class="op">*</span>(imap(<span class="bu">sum</span>, izip(<span class="op">*</span>channel)) <span class="cf">for</span> channel <span class="kw">in</span> channels)), nsamples)</span>
<span id="cb16-53"><a href="#cb16-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-54"><a href="#cb16-54" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> write_wavefile(filename, samples, nframes<span class="op">=</span><span class="va">None</span>, nchannels<span class="op">=</span><span class="dv">2</span>, sampwidth<span class="op">=</span><span class="dv">2</span>, framerate<span class="op">=</span><span class="dv">44100</span>, bufsize<span class="op">=</span><span class="dv">2048</span>):</span>
<span id="cb16-55"><a href="#cb16-55" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;Write samples to a wavefile.&quot;</span></span>
<span id="cb16-56"><a href="#cb16-56" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> nframes <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb16-57"><a href="#cb16-57" aria-hidden="true" tabindex="-1"></a>        nframes <span class="op">=</span> <span class="op">-</span><span class="dv">1</span></span>
<span id="cb16-58"><a href="#cb16-58" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-59"><a href="#cb16-59" aria-hidden="true" tabindex="-1"></a>    w <span class="op">=</span> wave.<span class="bu">open</span>(filename, <span class="st">'w'</span>)</span>
<span id="cb16-60"><a href="#cb16-60" aria-hidden="true" tabindex="-1"></a>    w.setparams((nchannels, sampwidth, framerate, nframes, <span class="st">'NONE'</span>, <span class="st">'not compressed'</span>))</span>
<span id="cb16-61"><a href="#cb16-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-62"><a href="#cb16-62" aria-hidden="true" tabindex="-1"></a>    max_amplitude <span class="op">=</span> <span class="bu">float</span>(<span class="bu">int</span>((<span class="dv">2</span> <span class="op">**</span> (sampwidth <span class="op">*</span> <span class="dv">8</span>)) <span class="op">/</span> <span class="dv">2</span>) <span class="op">-</span> <span class="dv">1</span>)</span>
<span id="cb16-63"><a href="#cb16-63" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-64"><a href="#cb16-64" aria-hidden="true" tabindex="-1"></a>    <span class="co"># split the samples into chunks (to reduce memory consumption and improve performance)</span></span>
<span id="cb16-65"><a href="#cb16-65" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> chunk <span class="kw">in</span> grouper(bufsize, samples):</span>
<span id="cb16-66"><a href="#cb16-66" aria-hidden="true" tabindex="-1"></a>        frames <span class="op">=</span> <span class="st">''</span>.join(<span class="st">''</span>.join(struct.pack(<span class="st">'h'</span>, <span class="bu">int</span>(max_amplitude <span class="op">*</span> sample)) <span class="cf">for</span> sample <span class="kw">in</span> channels) <span class="cf">for</span> channels <span class="kw">in</span> chunk <span class="cf">if</span> channels <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>)</span>
<span id="cb16-67"><a href="#cb16-67" aria-hidden="true" tabindex="-1"></a>        w.writeframesraw(frames)</span>
<span id="cb16-68"><a href="#cb16-68" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-69"><a href="#cb16-69" aria-hidden="true" tabindex="-1"></a>    w.close()</span>
<span id="cb16-70"><a href="#cb16-70" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-71"><a href="#cb16-71" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> filename</span>
<span id="cb16-72"><a href="#cb16-72" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-73"><a href="#cb16-73" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> write_pcm(f, samples, sampwidth<span class="op">=</span><span class="dv">2</span>, framerate<span class="op">=</span><span class="dv">44100</span>, bufsize<span class="op">=</span><span class="dv">2048</span>):</span>
<span id="cb16-74"><a href="#cb16-74" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;Write samples as raw PCM data.&quot;</span></span>
<span id="cb16-75"><a href="#cb16-75" aria-hidden="true" tabindex="-1"></a>    max_amplitude <span class="op">=</span> <span class="bu">float</span>(<span class="bu">int</span>((<span class="dv">2</span> <span class="op">**</span> (sampwidth <span class="op">*</span> <span class="dv">8</span>)) <span class="op">/</span> <span class="dv">2</span>) <span class="op">-</span> <span class="dv">1</span>)</span>
<span id="cb16-76"><a href="#cb16-76" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-77"><a href="#cb16-77" aria-hidden="true" tabindex="-1"></a>    <span class="co"># split the samples into chunks (to reduce memory consumption and improve performance)</span></span>
<span id="cb16-78"><a href="#cb16-78" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> chunk <span class="kw">in</span> grouper(bufsize, samples):</span>
<span id="cb16-79"><a href="#cb16-79" aria-hidden="true" tabindex="-1"></a>        frames <span class="op">=</span> <span class="st">''</span>.join(<span class="st">''</span>.join(struct.pack(<span class="st">'h'</span>, <span class="bu">int</span>(max_amplitude <span class="op">*</span> sample)) <span class="cf">for</span> sample <span class="kw">in</span> channels) <span class="cf">for</span> channels <span class="kw">in</span> chunk <span class="cf">if</span> channels <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>)</span>
<span id="cb16-80"><a href="#cb16-80" aria-hidden="true" tabindex="-1"></a>        f.write(frames)</span>
<span id="cb16-81"><a href="#cb16-81" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-82"><a href="#cb16-82" aria-hidden="true" tabindex="-1"></a>    f.close()</span>
<span id="cb16-83"><a href="#cb16-83" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-84"><a href="#cb16-84" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> filename</span>
<span id="cb16-85"><a href="#cb16-85" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-86"><a href="#cb16-86" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> main():</span>
<span id="cb16-87"><a href="#cb16-87" aria-hidden="true" tabindex="-1"></a>    parser <span class="op">=</span> argparse.ArgumentParser()</span>
<span id="cb16-88"><a href="#cb16-88" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'-c'</span>, <span class="st">'--channels'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;Number of channels to produce&quot;</span>, default<span class="op">=</span><span class="dv">2</span>, <span class="bu">type</span><span class="op">=</span><span class="bu">int</span>)</span>
<span id="cb16-89"><a href="#cb16-89" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'-b'</span>, <span class="st">'--bits'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;Number of bits in each sample&quot;</span>, choices<span class="op">=</span>(<span class="dv">16</span>,), default<span class="op">=</span><span class="dv">16</span>, <span class="bu">type</span><span class="op">=</span><span class="bu">int</span>)</span>
<span id="cb16-90"><a href="#cb16-90" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'-r'</span>, <span class="st">'--rate'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;Sample rate in Hz&quot;</span>, default<span class="op">=</span><span class="dv">44100</span>, <span class="bu">type</span><span class="op">=</span><span class="bu">int</span>)</span>
<span id="cb16-91"><a href="#cb16-91" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'-t'</span>, <span class="st">'--time'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;Duration of the wave in seconds.&quot;</span>, default<span class="op">=</span><span class="dv">60</span>, <span class="bu">type</span><span class="op">=</span><span class="bu">int</span>)</span>
<span id="cb16-92"><a href="#cb16-92" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'-a'</span>, <span class="st">'--amplitude'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;Amplitude of the wave on a scale of 0.0-1.0.&quot;</span>, default<span class="op">=</span><span class="fl">0.5</span>, <span class="bu">type</span><span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb16-93"><a href="#cb16-93" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'-f'</span>, <span class="st">'--frequency'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;Frequency of the wave in Hz&quot;</span>, default<span class="op">=</span><span class="fl">440.0</span>, <span class="bu">type</span><span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb16-94"><a href="#cb16-94" aria-hidden="true" tabindex="-1"></a>    parser.add_argument(<span class="st">'filename'</span>, <span class="bu">help</span><span class="op">=</span><span class="st">&quot;The file to generate.&quot;</span>)</span>
<span id="cb16-95"><a href="#cb16-95" aria-hidden="true" tabindex="-1"></a>    args <span class="op">=</span> parser.parse_args()</span>
<span id="cb16-96"><a href="#cb16-96" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-97"><a href="#cb16-97" aria-hidden="true" tabindex="-1"></a>    <span class="co"># each channel is defined by infinite functions which are added to produce a sample.</span></span>
<span id="cb16-98"><a href="#cb16-98" aria-hidden="true" tabindex="-1"></a>    channels <span class="op">=</span> ((sine_wave(args.frequency, args.rate, args.amplitude),) <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(args.channels))</span>
<span id="cb16-99"><a href="#cb16-99" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-100"><a href="#cb16-100" aria-hidden="true" tabindex="-1"></a>    <span class="co"># convert the channel functions into waveforms</span></span>
<span id="cb16-101"><a href="#cb16-101" aria-hidden="true" tabindex="-1"></a>    samples <span class="op">=</span> compute_samples(channels, args.rate <span class="op">*</span> args.time)</span>
<span id="cb16-102"><a href="#cb16-102" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-103"><a href="#cb16-103" aria-hidden="true" tabindex="-1"></a>    <span class="co"># write the samples to a file</span></span>
<span id="cb16-104"><a href="#cb16-104" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> args.filename <span class="op">==</span> <span class="st">'-'</span>:</span>
<span id="cb16-105"><a href="#cb16-105" aria-hidden="true" tabindex="-1"></a>        filename <span class="op">=</span> sys.stdout</span>
<span id="cb16-106"><a href="#cb16-106" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span>:</span>
<span id="cb16-107"><a href="#cb16-107" aria-hidden="true" tabindex="-1"></a>        filename <span class="op">=</span> args.filename</span>
<span id="cb16-108"><a href="#cb16-108" aria-hidden="true" tabindex="-1"></a>    write_wavefile(filename, samples, args.rate <span class="op">*</span> args.time, args.channels, args.bits <span class="op">/</span> <span class="dv">8</span>, args.rate)</span>
<span id="cb16-109"><a href="#cb16-109" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-110"><a href="#cb16-110" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb16-111"><a href="#cb16-111" aria-hidden="true" tabindex="-1"></a>    main()</span></code></pre></div>
<p>You can execute the file directly and it will generate a pure sine tone.</p>
<h2 id="examples">Examples</h2>
<h3 id="sbagen">SBaGen</h3>
<p><a href="http://uazu.net/sbagen/">SBaGen</a> is a program which generates binaural beats. It’s reasonably complex, consisting
of around 3000 lines of C. You can instruct SBaGen to generate a 200Hz pure sine tone in one
channel and a 204Hz pure sine tone in the other channel by doing this:</p>
<pre class="console"><code>$ sbagen -i 202+2/10</code></pre>
<p>You can also generate multiple binaural beats simultaneously:</p>
<pre class="console"><code>$ sbagen -i 202+2/10 400+20/10</code></pre>
<p>The following emulates <code>sbagen -i</code>, but it’s about 100x slower. No problem for
real-time use on a modern computer, but you’re better off using the real deal
for serious use.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> re</span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> wavebender <span class="im">import</span> <span class="op">*</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> itertools <span class="im">import</span> <span class="op">*</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> sbagen_phrase(phrase):</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">'''</span></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a><span class="co">    147.0+4.0/1.27 -&gt; two sine_waves. one 145.0 hz; one 149.0 hz. each at amplitude of 0.0127.</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="co">    '''</span></span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="st">'pink'</span> <span class="kw">in</span> phrase:</span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a>        <span class="co"># pink/40 -&gt; white_noise(amplitude=0.4)</span></span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a>        amplitude <span class="op">=</span> <span class="bu">float</span>(phrase.split(<span class="st">'/'</span>)[<span class="op">-</span><span class="dv">1</span>]) <span class="op">/</span> <span class="fl">100.0</span></span>
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> (white_noise(amplitude),</span>
<span id="cb19-15"><a href="#cb19-15" aria-hidden="true" tabindex="-1"></a>                white_noise(amplitude))</span>
<span id="cb19-16"><a href="#cb19-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-17"><a href="#cb19-17" aria-hidden="true" tabindex="-1"></a>    carrier, remainder <span class="op">=</span> re.split(<span class="st">'[+-]'</span>, phrase, <span class="dv">1</span>)</span>
<span id="cb19-18"><a href="#cb19-18" aria-hidden="true" tabindex="-1"></a>    beatfreq, amplitude <span class="op">=</span> remainder.split(<span class="st">'/'</span>)</span>
<span id="cb19-19"><a href="#cb19-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-20"><a href="#cb19-20" aria-hidden="true" tabindex="-1"></a>    carrier <span class="op">=</span> <span class="bu">float</span>(carrier)</span>
<span id="cb19-21"><a href="#cb19-21" aria-hidden="true" tabindex="-1"></a>    beatfreq <span class="op">=</span> <span class="bu">float</span>(beatfreq)</span>
<span id="cb19-22"><a href="#cb19-22" aria-hidden="true" tabindex="-1"></a>    amplitude <span class="op">=</span> <span class="bu">float</span>(amplitude) <span class="op">/</span> <span class="fl">100.0</span></span>
<span id="cb19-23"><a href="#cb19-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-24"><a href="#cb19-24" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> (sine_wave((carrier <span class="op">-</span> beatfreq<span class="op">/</span><span class="dv">2</span>), amplitude<span class="op">=</span>amplitude),</span>
<span id="cb19-25"><a href="#cb19-25" aria-hidden="true" tabindex="-1"></a>            sine_wave((carrier <span class="op">+</span> beatfreq<span class="op">/</span><span class="dv">2</span>), amplitude<span class="op">=</span>amplitude))</span>
<span id="cb19-26"><a href="#cb19-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-27"><a href="#cb19-27" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> sbagen_line(line, length<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb19-28"><a href="#cb19-28" aria-hidden="true" tabindex="-1"></a>    <span class="co">'''</span></span>
<span id="cb19-29"><a href="#cb19-29" aria-hidden="true" tabindex="-1"></a><span class="co">    Given a sequence of (l, r), (l, r), return a sequence of (l, l), (r, r).</span></span>
<span id="cb19-30"><a href="#cb19-30" aria-hidden="true" tabindex="-1"></a><span class="co">    '''</span></span>
<span id="cb19-31"><a href="#cb19-31" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> izip(<span class="op">*</span>(imap(<span class="kw">lambda</span> s: islice(s, length), sbagen_phrase(phrase)) <span class="cf">for</span> phrase <span class="kw">in</span> line.split()))</span>
<span id="cb19-32"><a href="#cb19-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-33"><a href="#cb19-33" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> sys.argv[<span class="dv">1</span>:]:</span>
<span id="cb19-34"><a href="#cb19-34" aria-hidden="true" tabindex="-1"></a>    channels <span class="op">=</span> sbagen_line(<span class="st">' '</span>.join(sys.argv[<span class="dv">1</span>:]))</span>
<span id="cb19-35"><a href="#cb19-35" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span>:</span>
<span id="cb19-36"><a href="#cb19-36" aria-hidden="true" tabindex="-1"></a>    sys.exit(<span class="dv">1</span>)</span>
<span id="cb19-37"><a href="#cb19-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-38"><a href="#cb19-38" aria-hidden="true" tabindex="-1"></a>samples <span class="op">=</span> compute_samples(channels)</span>
<span id="cb19-39"><a href="#cb19-39" aria-hidden="true" tabindex="-1"></a>write_wavefile(sys.stdout, samples)</span></code></pre></div>
<p>You can use it like this:</p>
<pre class="console"><code>$ ./sbagen.py 272.2+7.83/10 332+7.83/10 421.3+7.83/10 289.4+7.83/10 367.5+7.83/10 442+7.83/10 295.7+7.83/10 414.7+7.83/10 422+7.83/10 | aplay</code></pre>
<p>That generates a bunch of simultaneous binaural tones and uses <code>aplay</code>
to play them in realtime.</p>
<h3 id="melody">Melody</h3>
<p>Here’s an example of a melody, using <code>itertools</code> and <code>wavebender</code>.</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> wavebender <span class="im">import</span> <span class="op">*</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> itertools <span class="im">import</span> <span class="op">*</span></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> sys</span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> ncycles(iterable, n):</span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;Returns the sequence elements n times&quot;</span></span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> chain.from_iterable(repeat(<span class="bu">tuple</span>(iterable), n))</span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> waves():</span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a>    l <span class="op">=</span> <span class="bu">int</span>(<span class="dv">44100</span><span class="op">*</span><span class="fl">0.4</span>) <span class="co"># each note lasts 0.4 seconds</span></span>
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> cycle(chain(ncycles(chain(islice(damped_wave(frequency<span class="op">=</span><span class="fl">440.0</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l),</span>
<span id="cb21-14"><a href="#cb21-14" aria-hidden="true" tabindex="-1"></a>                                     islice(damped_wave(frequency<span class="op">=</span><span class="fl">261.63</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l),</span>
<span id="cb21-15"><a href="#cb21-15" aria-hidden="true" tabindex="-1"></a>                                     islice(damped_wave(frequency<span class="op">=</span><span class="fl">329.63</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l)), <span class="dv">3</span>),</span>
<span id="cb21-16"><a href="#cb21-16" aria-hidden="true" tabindex="-1"></a>                       islice(damped_wave(frequency<span class="op">=</span><span class="fl">440.0</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="dv">3</span><span class="op">*</span>l), <span class="dv">3</span><span class="op">*</span>l),</span>
<span id="cb21-17"><a href="#cb21-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-18"><a href="#cb21-18" aria-hidden="true" tabindex="-1"></a>                       ncycles(chain(islice(damped_wave(frequency<span class="op">=</span><span class="fl">293.66</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l),</span>
<span id="cb21-19"><a href="#cb21-19" aria-hidden="true" tabindex="-1"></a>                                     islice(damped_wave(frequency<span class="op">=</span><span class="fl">261.63</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l),</span>
<span id="cb21-20"><a href="#cb21-20" aria-hidden="true" tabindex="-1"></a>                                     islice(damped_wave(frequency<span class="op">=</span><span class="fl">293.66</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l)), <span class="dv">2</span>),</span>
<span id="cb21-21"><a href="#cb21-21" aria-hidden="true" tabindex="-1"></a>                       chain(islice(damped_wave(frequency<span class="op">=</span><span class="fl">293.66</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l),</span>
<span id="cb21-22"><a href="#cb21-22" aria-hidden="true" tabindex="-1"></a>                             islice(damped_wave(frequency<span class="op">=</span><span class="fl">329.63</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l),</span>
<span id="cb21-23"><a href="#cb21-23" aria-hidden="true" tabindex="-1"></a>                             islice(damped_wave(frequency<span class="op">=</span><span class="fl">293.66</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="bu">int</span>(l<span class="op">/</span><span class="dv">4</span>)), l)),</span>
<span id="cb21-24"><a href="#cb21-24" aria-hidden="true" tabindex="-1"></a>                       islice(damped_wave(frequency<span class="op">=</span><span class="fl">261.63</span>, amplitude<span class="op">=</span><span class="fl">0.1</span>, length<span class="op">=</span><span class="dv">3</span><span class="op">*</span>l), <span class="dv">3</span><span class="op">*</span>l)))</span>
<span id="cb21-25"><a href="#cb21-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-26"><a href="#cb21-26" aria-hidden="true" tabindex="-1"></a>channels <span class="op">=</span> ((waves(),), (waves(), white_noise(amplitude<span class="op">=</span><span class="fl">0.001</span>),))</span>
<span id="cb21-27"><a href="#cb21-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-28"><a href="#cb21-28" aria-hidden="true" tabindex="-1"></a>samples <span class="op">=</span> compute_samples(channels, <span class="va">None</span>)</span>
<span id="cb21-29"><a href="#cb21-29" aria-hidden="true" tabindex="-1"></a>write_wavefile(sys.stdout, samples, <span class="va">None</span>)</span></code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Well, there’s your daily abuse of <code>itertools</code>. For more <code>itertools</code>
madness, check out my <a href="../project-euler-solutions/">Project Euler Solutions</a>.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>Build a Media Server With Ampache and Transmission</title>
    <link href="https://zach.se/build-a-media-server-with-ampache-and-transmission/" />
    <id>https://zach.se/build-a-media-server-with-ampache-and-transmission/</id>
    <published>2011-04-12T00:00:00Z</published>
    <updated>2011-04-12T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>Build a Media Server With Ampache and Transmission - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="A guide to building the ultimate music server using open source software.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">Build a Media Server With Ampache and Transmission</a>
</h1>
<section class="page-contents">
    <article>
        <p>This guide will teach you how to unleash your music collection. Access
it from anywhere with an internet connection – on your laptop, your phone,
at work, or on a plane.</p>
<p>This guide has three parts. Part one tells you how to build a server for
less than $300. Part two discusses how to build a music collection.
Part three discusses how to stream that collection across the internet.
<!--more--></p>
<p>If you already have a computer that is connected to the Internet 24/7,
you can skip part one.</p>
<h2 id="part-one-building-a-server">Part One: Building a Server</h2>
<p>We will need a computer to use as the server. You have three options
here:</p>
<ol type="1">
<li>Re-use an old computer. The cheapest option.</li>
<li>Purchase a new computer. The easiest option.</li>
<li>Build a new computer. The educational option.</li>
<li>Purchase a <abbr title="Virtual private server">VPS</abbr>. Potentially the best option, with a few caveats (<a href="#vps-note">more details below</a>).</li>
</ol>
<p>If you chose option 3, read the following section. Otherwise you can
skip past it.</p>
<h3 id="building-a-new-computer">Building a new computer</h3>
<p>The first thing to do when building a new computer is to come up with a
parts list. It turns out that there are only six things you need to build
a computer:</p>
<ul>
<li>CPU (aka processor)</li>
<li>Motherboard</li>
<li>RAM (aka memory)</li>
<li>Storage drive(s)</li>
<li>Power supply</li>
<li>Case</li>
</ul>
<p>Seasoned builders will be quick to point that there are a plethora of
additional parts that can be used to build a computer. Expert builders will
be even quicker to point out that you don’t even need a case
or storage drives. For the purposes of this guide, however, I will focus
on the six aforementioned parts. What follows is a list of the six parts
I used to build my media server. They will most likely be out of date by
the time you read this – just use them as an example.</p>
<h4 id="cpu-amd-athlon-ii-x2-250-60.99">CPU: AMD Athlon II X2 250 ($60.99)</h4>
<p>You don’t need a super-powerful processor in your server. That said,
this processor packs an astonishing punch at 60 bucks.</p>
<h4 id="motherboard-gigabyte-ga-ma74gm-s2-740g-54.99">Motherboard: Gigabyte GA-MA74GM-S2 740G ($54.99)</h4>
<p>I’m really pleased with this motherboard. It’s very small (Micro ATX),
yet it comes with a ton of features like onboard video, gigabit
ethernet, sound, and the ability to upgrade to a discrete graphics card
if desired.</p>
<p>If you use this motherboard (or similar), your media server
will be able to perform double-duty as a media center if you hook it up
to your TV. Expect to see another guide explaining how to do this in the
future.</p>
<h4 id="ram-a-data-2gb-ddr2-800-32.99">RAM: A-DATA 2GB DDR2-800 ($32.99)</h4>
<p>Just some basic, cheap DDR2 RAM. Works well (did I mention it was
cheap?).</p>
<h4 id="storage-drive-samsung-spinpoint-f4-2tb-5400-rpm-79.99">Storage drive: Samsung Spinpoint F4 2TB 5400 RPM ($79.99)</h4>
<p>You’re going to want a large drive in your media server to store your
music collection. 2TB for 80 bucks is just an incredible deal. Less than three
years ago I paid the same price for 10% of that.</p>
<h4 id="power-supply-logisys-575w-24.99">Power supply: Logisys 575W ($24.99)</h4>
<p>Normally I use high-end power supplies when building computers, so I was
a bit wary of using this $25 power supply. It was substantially lighter
than I am used to (heftier power supplies are typically higher-quality),
but so far it has performed admirably.</p>
<h4 id="case-apex-tx-381-c-micro-atx-tower-29.99">Case: Apex TX-381-C Micro ATX Tower ($29.99)</h4>
<p>It’s a solid case. Very inexpensive. Somewhat loud.</p>
<p>That’s a grand total of around 280 bucks for a great server. Today you can
probably get an even better machine for the same price or less.</p>
<p>Once you have all of the parts, you will need to put them together.
Check out <a href="http://en.wikibooks.org/wiki/How_To_Assemble_A_Desktop_PC">How To Assemble A Desktop PC</a>, a great wikibook teaching
you how to do that.</p>
<h3 id="setting-up-the-server">Setting up the server</h3>
<p>Now that you have a machine to use as your media server, you will need
to install Linux on it. The version (or “distro”) I recommend is Ubuntu
Server Edition. Take a look at
the <a href="https://help.ubuntu.com/10.10/serverguide/C/index.html">Ubuntu Server Guide</a>.
which will teach you everything you need to know.</p>
<p>You’re going to want to set up SSH access to your machine so that you
can manage it remotely. You can find the details in the
<a href="https://help.ubuntu.com/10.10/serverguide/C/openssh-server.html">OpenSSH section of the Ubuntu Server Guide</a>.</p>
<p>The final thing you need to do is to hook your new server up to the
Internet. For streaming 320kbps music files, I recommend having a minimum
upload bandwidth of around 384kbps (50KB/s). That’s what I used to have,
and occasionally I had to reduce the streaming rate to 192kbps to
eliminate stuttering. Miraculously my ISP increased my upload bandwidth to around 1mbps,
for free (as far as I know). That’s more than enough for streaming music.</p>
<h3 id="vps-note">
A note on VPSs
</h3>
<p>If you opt to use a VPS, you don’t have to worry about setting up a
Linux server with a 24/7 high speed internet connection. What you do
need to worry about is violating your hosting provider’s Terms of
Service. They may not appreciate you downloading and streaming music on
their servers.</p>
<p>The other thing is that you likely won’t have unlimited disk space, even
if you do have “Unlimited Disk Space!”. I would think twice before
uploading a 400GB FLAC collection to a VPS.</p>
<p>If you can deal with these two conditions, then using your VPS as a media
server is probably the best way to go. You get an extremely fast
connection and you don’t have to worry as much about maintaining the
server.</p>
<h2 id="part-two-building-a-music-collection">Part Two: Building a music collection</h2>
<p>What good is a music server without any music? That’s a problem that
this section of the guide intends to solve. There are a lot of ways to
get music these days: iTunes, purchasing directly from the artist,
BitTorrent, and even purchasing from a store. For simplicity, I’m just
going to focus on BitTorrent. If you don’t know what
BitTorrent is, you may want to read <a href="http://www.bittorrent.org/introduction.html">this introduction</a> first.</p>
<p>This section of the guide will teach you how to access and use the best
music repositories in the world (for free).</p>
<h3 id="installing-the-bittorrent-client">Installing the BitTorrent client</h3>
<p>You may be familiar with Transmission – it’s an excellent, easy-to-use
BitTorrent client for Windows, Mac, and Linux. Transmission can also run
in the background, as a daemon. This is called <code>transmission-daemon</code>.
<code>transmission-daemon</code> comes with a beautiful web interface which you will
be using to manage your torrents from anywhere in the world.</p>
<p>To install <code>transmission-daemon</code> on Ubuntu, ssh into your server and type the following:</p>
<pre class="console"><code>$ sudo aptitude install transmission-daemon</code></pre>
<p>First we make sure that <code>transmission-daemon</code> is not running:</p>
<pre class="console"><code>$ sudo /etc/init.d/transmission-daemon stop</code></pre>
<p>Now you want to configure <code>transmission-daemon</code>, so do:</p>
<pre class="console"><code>$ sudo $EDITOR /etc/transmission-daemon/settings.json</code></pre>
<p>You want to make the following changes:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;rpc-whitelist-enabled&quot;</span><span class="op">:</span> <span class="kw">false</span><span class="op">,</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;rpc-username&quot;</span><span class="op">:</span> <span class="st">&quot;your-username&quot;</span><span class="op">,</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;rpc-password&quot;</span><span class="op">:</span> <span class="st">&quot;your-password&quot;</span><span class="op">,</span></span></code></pre></div>
<p>This allows you to access <code>transmission-daemon</code> remotely, as long
as you specify the correct username and password.</p>
<p>Finally, we restart <code>transmission-daemon</code>:</p>
<pre class="console"><code>$ sudo /etc/init.d/transmission-daemon start</code></pre>
<p>Now you can log in to the <code>transmission-daemon</code> web interface by pointing your web browser to
http://your-server:9091/. Once you’ve logged in, you should see a nice
interface where you can manage torrents and edit preferences. This is
how you’ll be interacting with <code>transmission-daemon</code> most of the time.</p>
<p>What we need now is some torrents to download!</p>
<h3 id="finding-music-for-your-collection">Finding music for your collection</h3>
<p>I usually find new music based on recommendations from friends or
Last.fm. Last.fm is a service which monitors your listening habits and
then provides recommendations based on that data. It’s pretty cool, and
it makes it easier to answer “So, what kind of music do you like?”
with something other than “Oh, I listen to a little bit of everything.”.</p>
<p>Once you have some candidates for adding to your collection, you need to
actually get your hands on the files. To do this, we use a
BitTorrent search engine. The three that I use are (ranked from decent to
best):</p>
<ul>
<li>The Pirate Bay</li>
<li>Demonoid</li>
<li>What.CD</li>
</ul>
<h4 id="the-pirate-bay">The Pirate Bay</h4>
<p>Most BitTorrent users will be familiar with The Pirate Bay. It’s one of
the largest BitTorrent sites and certainly the most infamous. They offer
a fair amount of music, probably enough for the casual user. There are
no real quality standards here so you may have to search a bit before
finding the good stuff. The Pirate Bay does not require registration.</p>
<h4 id="demonoid">Demonoid</h4>
<p>Demonoid is similar to The Pirate Bay in that it offers a wide range of
content. I often find that if something isn’t on TPB, it’s on Demonoid
(and vice versa). Demonoid requires registration, but it’s not difficult
to get in.</p>
<h4 id="what.cd">What.CD</h4>
<p>What.CD is the holy grail for music enthusiasts. It is indisputably the
greatest music collection ever assembled. If you’re lucky enough to get
in, you will have access to pretty much every release of every album in
any format you could want. They maintain their high quality by
maintaining a high quality userbase, so if you want to get in you have
two options: get an invite or pass an interview.</p>
<h3 id="a-brief-digression-on-formats">A brief digression on formats</h3>
<p>There are two kinds of music formats: lossy and lossless.</p>
<p>MP3, AAC, and OGG are all lossy formats, meaning that they discard some of the audio
data in order to achieve smaller file sizes. Lossy formats are prevalent
on public trackers like The Pirate Bay.</p>
<p>FLAC is a lossless format, meaning that it preserves all of the original
audio data. As you build your music collection, you will learn more and
more about FLAC. My collection consists solely of FLACs.
One cool thing about FLAC that relates to this guide is that you can convert it to a
lossy format on-the-fly for streaming over the internet – useful if you
have a slow connection.</p>
<h2 id="part-three-serving-your-music-collection-over-the-internet">Part three: Serving your music collection over the Internet</h2>
<p>To stream your music over the Internet we will be using something called
Ampache. The name is a portmanteau of the words Amp (as in amplifier)
and Apache (the prominent web server).</p>
<p>Ampache provides a web interface to your music collection. You use the
interface to create playlists. The playlists can then be played in the
browser or opened in your favorite standalone media player. There are
also Ampache clients for Android and iPhone, so you can access your full
music collection on your phone. Additionally, Ampache can transcode your
music on-the-fly to any format (useful for streaming to devices with
slower internet connections).</p>
<h3 id="installing-ampache">Installing Ampache</h3>
<p>Ampache is included in the Ubuntu repositories. Just do:</p>
<pre class="console"><code>$ sudo aptitude install ampache mysql-server</code></pre>
<p>And the required programs will be installed for you. When asked which
web server to configure automatically, select apache2. When asked to
restart the web server, select “Yes”. Choose the root password for your
MySQL server and remember it.</p>
<p>At this point, try navigating to http://your-server/ampache/. If the
page is not found, do the following:</p>
<pre class="console"><code>$ sudo $EDITOR /etc/apache2/apache2.conf</code></pre>
<p>Add the following to the bottom of that file:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode apache"><code class="sourceCode apache"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>Include<span class="st"> /etc/ampache/ampache.conf</span></span></code></pre></div>
<p>Restart the web server:</p>
<pre class="console"><code>$ sudo /etc/init.d/apache2 restart</code></pre>
<p>Now, open http://your-server/ampache/ in a web browser. You will see a
webpage entitled ‘Ampache Installation’. Click on ‘Start configuration’
near the bottom of the page.</p>
<p>On the next page, enter the MySQL root password in the ‘MySQL
Administrative Password’ box. Then click ‘Insert Database’.</p>
<p>On this page, type ‘root’ into the ‘MySQL Username’ box. The ‘MySQL
username’ is the MySQL root password you entered on the previous page.
Click ‘Write Config’ and you will be prompted to download an
<code>ampache.cfg.php</code> file. Save this file to your local machine.</p>
<h3 id="configuring-ampache">Configuring Ampache</h3>
<p>Make the following changes to <code>ampache.cfg.php</code>:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="fu">xml_rpc</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;true&quot;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="fu">allow_zip_download</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;true&quot;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="fu">transcode_m4a</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;false&quot;</span></span></code></pre></div>
<p>If you would like to transcode your FLACs on-the-fly, make the following
change:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="fu">transcode_flac</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;true&quot;</span></span></code></pre></div>
<p>If you have multiple formats of each album (e.g. if you download albums in
MP3, OGG, AAC, and FLAC), each format will be indexed. To eliminate these
duplicates, you can opt to only index one of these formats. To only
index FLAC:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="fu">catalog_file_pattern</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;flac&quot;</span></span></code></pre></div>
<p>When you’ve finished editing <code>ampache.cfg.php</code>, copy it to the
<code>/etc/ampache</code> directory on your server:</p>
<pre class="console"><code>$ scp /path/to/ampache.cfg.php root@your-server:/etc/ampache/</code></pre>
<p>Once <code>ampache.cfg.php</code> is in place on your server, click ‘Check for
Config’. Both ‘Ampache.cfg.php Exists’ and ‘Ampache.cfg.php Configured?’
should now say ‘OK’. Click ‘Continue to Step 3’.</p>
<p>Finally, setup the administrator account. Once you’ve filled out the
form, click ‘Create Account’.</p>
<p>Login to your Ampache installation as the administrator. We will now
configure Ampache to scan your music collection.</p>
<p>On the menu at the left of the page, click the ‘Admin’ icon (looks like
a small server). Then click ‘Add a Catalog’.</p>
<p>The catalog name can be anything you like. ‘Path’ should be set to to
the path of your music location. If you are using <code>transmission-daemon</code>
and would like all of its downloads to be indexed, use
<code>/var/lib/transmission-daemon/downloads/</code>. Then click ‘Add Catalog’.</p>
<p>Once your music collection has been indexed, click ‘Continue’. Now click
on the ‘Preferences’ icon in the menu on the left of the page.</p>
<p>If you have opted to transcode FLACs on-the-fly, click on ‘Streaming’
and set your ‘Transcode Bitrate’ to something more
reasonable, like 320. Click ‘Update Preferences’ and you’re done.</p>
<h3 id="using-ampache">Using Ampache</h3>
<p>Now you’re ready to use Ampache. Using Ampache is basically a two-step
process. First you queue some music into a playlist. Then you open that
playlist in a media player which handles the streaming for you.</p>
<h4 id="queueing-music">Queueing Music</h4>
<p>Click on the “Home” icon in the menu on the left. Click on “Albums” to
view a list of your albums. If you click the green plus icon next to
any album title, it will be loaded into your playlist. You’ll see the
tracks in the playlist view on the right side of the screen.</p>
<h4 id="opening-the-playlist">Opening the playlist</h4>
<p>Now that you have some music in the playlist, click the “Play” icon
(looks like a broadcast tower) located above the playlist on the right
side of the screen. You will be prompted to download a playlist file.
Download it and open it with the media player of your choice – Totem or
VLC for example. You should hear some music. Enjoy!</p>
<h2 id="conclusion">Conclusion</h2>
<p>Hopefully you now have enough information to build a killer media
server. I covered a lot of ground in this guide, so feel free to ask any
questions you may have.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>Geodesic Distance Between Points in GeoDjango</title>
    <link href="https://zach.se/geodesic-distance-between-points-in-geodjango/" />
    <id>https://zach.se/geodesic-distance-between-points-in-geodjango/</id>
    <published>2011-02-17T00:00:00Z</published>
    <updated>2011-02-17T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>Geodesic Distance Between Points in GeoDjango - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="Various ways to perform geodesic distance calculations in Django.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">Geodesic Distance Between Points in GeoDjango</a>
</h1>
<section class="page-contents">
    <article>
        <h2 id="background">Background</h2>
<p>I’m working on a Django project which deals with geographic data. To
handle this data, I’m using GeoDjango (django.contrib.gis), which is a
fantastic framework for working with GIS information in Django. The data
I am using consists of points scattered across the Earth, so I decided
to store the geographic information in the WGS84 datum (SRID 4326), the
system used by GPS.</p>
<p>The problem is that since WGS84 is a geographic coordinate system, as
opposed to a projected coordinate system, GeoDjango returns the
Cartesian distance between points as a float value in degrees. This is
not useful for presenting to users, since we deal with kilometers and
miles in everyday life. The distance we want is known as the <a href="http://en.wikipedia.org/wiki/Geodesy" title="Geodesy">geodesic
distance</a>.</p>
<h2 id="geodesic-distance">Geodesic Distance</h2>
<p>Geodesic distance refers to the distance between two points if you were
to travel between them on the Earth’s surface. Thus, it is the ‘real’
distance between two points, as opposed to the theoretical distance
returned by GeoDjango.</p>
<p>The formulae used to calculate this distance are <a href="http://en.wikipedia.org/wiki/Vincenty%27s_formulae" title="Vincenty's formulae">quite complex</a>.
Luckily, they have been implemented for us in a Python module called
<a href="http://code.google.com/p/geopy/wiki/GettingStarted#Calculating_distances" title="Calculating Distances with geopy">geopy</a>. The specific method we will be using is known as Vincenty’s
method. It’s accurate to within 0.5mm!</p>
<p>The specific function is <code>geopy.distance.distance</code>. Here’s a simple
example demonstrating its use:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="im">from</span> geopy.distance <span class="im">import</span> distance <span class="im">as</span> geopy_distance</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> washington <span class="op">=</span> (<span class="fl">38.53</span>, <span class="fl">77.02</span>)</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> chicago <span class="op">=</span> (<span class="fl">41.50</span>, <span class="fl">87.37</span>)</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> d <span class="op">=</span> geopy_distance(washington, chicago)</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="bu">print</span> d.meters</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="fl">942408.377797</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="bu">print</span> d.miles</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="fl">585.585417063</span></span></code></pre></div>
<p>Notice how converting between different units of measure on the
resultant <code>Distance</code> object is as simple as accessing different
attributes of that object (in this case, <code>meters</code> and <code>miles</code>).</p>
<h2 id="solution">Solution</h2>
<p>In the example below, we have a Route object which can be associated
with an arbitrary number of waypoints. Since the geopy distance function
only works on two points, we use the itertools module to take the points
pairwise so that we can calculate the distance for each pair and find
the sum of the distances. Once we have calculated the geodesic distance
with geopy and itertools, we turn it into a GeoDjango Distance object,
which allows us to seamlessly convert to several different kinds of
units as well as interface with the rest of GeoDjango’s spatial query
functions.</p>
<p>Here’s an example <code>models.py</code> which demonstrates this approach:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> itertools <span class="im">import</span> tee, izip</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.contrib.gis.db <span class="im">import</span> models</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.contrib.gis.measure <span class="im">import</span> Distance</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> geopy.distance <span class="im">import</span> distance <span class="im">as</span> geopy_distance</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> pairwise(iterable):</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;s -&gt; (s0,s1), (s1,s2), (s2, s3), ...&quot;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>    a, b <span class="op">=</span> tee(iterable)</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>    <span class="bu">next</span>(b, <span class="va">None</span>)</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> izip(a, b)</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="co"># Create your models here.</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Route(models.Model):</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>    name <span class="op">=</span> models.CharField(max_length<span class="op">=</span><span class="dv">100</span>)</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> length(<span class="va">self</span>):</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>        <span class="co">'''Determine the length of the route.'''</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>        points <span class="op">=</span> (waypoint.point <span class="cf">for</span> waypoint <span class="kw">in</span> <span class="va">self</span>.waypoint_set.orderby(<span class="st">'time'</span>))</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>        meters <span class="op">=</span> <span class="bu">sum</span>(geopy_distance(a, b).meters <span class="cf">for</span> (a, b) <span class="kw">in</span> pairwise(points))</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> Distance(m<span class="op">=</span>meters)</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Waypoint(models.Model):</span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>    time <span class="op">=</span> models.DateTimeField()</span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a>    route <span class="op">=</span> models.ForeignKey(Route)</span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a>    point <span class="op">=</span> models.PointField(srid<span class="op">=</span><span class="dv">4326</span>)</span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a>    objects <span class="op">=</span> models.GeoManager()</span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> distance(<span class="op">*</span>points):</span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a>    <span class="co">'''</span></span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a><span class="co">    Find the geodesic distance between two or more points.</span></span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-34"><a href="#cb2-34" aria-hidden="true" tabindex="-1"></a><span class="co">    If more than two points are specified, the points are assumed</span></span>
<span id="cb2-35"><a href="#cb2-35" aria-hidden="true" tabindex="-1"></a><span class="co">    to be on a route. The total length of this route is</span></span>
<span id="cb2-36"><a href="#cb2-36" aria-hidden="true" tabindex="-1"></a><span class="co">    calculated.</span></span>
<span id="cb2-37"><a href="#cb2-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-38"><a href="#cb2-38" aria-hidden="true" tabindex="-1"></a><span class="co">    Returns a django.contrib.gis.measure.Distance object.</span></span>
<span id="cb2-39"><a href="#cb2-39" aria-hidden="true" tabindex="-1"></a><span class="co">    '''</span></span>
<span id="cb2-40"><a href="#cb2-40" aria-hidden="true" tabindex="-1"></a>    meters <span class="op">=</span> <span class="bu">sum</span>(geopy_distance(a, b).meters <span class="cf">for</span> (a, b) <span class="kw">in</span> pairwise(points))</span>
<span id="cb2-41"><a href="#cb2-41" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> Distance(m<span class="op">=</span>meters)</span>
<span id="cb2-42"><a href="#cb2-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-43"><a href="#cb2-43" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> example():</span>
<span id="cb2-44"><a href="#cb2-44" aria-hidden="true" tabindex="-1"></a>    <span class="im">from</span> django.contrib.gis.geos <span class="im">import</span> Point</span>
<span id="cb2-45"><a href="#cb2-45" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Calculate the distance to San Franciso from Washington, D.C., via Chicago.</span></span>
<span id="cb2-46"><a href="#cb2-46" aria-hidden="true" tabindex="-1"></a>    washington <span class="op">=</span> Point(<span class="fl">38.53</span>, <span class="fl">77.02</span>)</span>
<span id="cb2-47"><a href="#cb2-47" aria-hidden="true" tabindex="-1"></a>    chicago <span class="op">=</span> Point(<span class="fl">41.50</span>, <span class="fl">87.37</span>)</span>
<span id="cb2-48"><a href="#cb2-48" aria-hidden="true" tabindex="-1"></a>    san_francisco <span class="op">=</span> Point(<span class="fl">37.47</span>, <span class="fl">122.26</span>)</span>
<span id="cb2-49"><a href="#cb2-49" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-50"><a href="#cb2-50" aria-hidden="true" tabindex="-1"></a>    d <span class="op">=</span> distance(washington, chicago, san_francisco)</span>
<span id="cb2-51"><a href="#cb2-51" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span> <span class="st">&quot;Washington, D.C. -&gt; Chicago -&gt; San Francisco:&quot;</span></span>
<span id="cb2-52"><a href="#cb2-52" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span> <span class="st">&quot;</span><span class="sc">%(miles)0.2f</span><span class="st"> miles, or </span><span class="sc">%(kilometers)0.2f</span><span class="st"> kilometers (or </span><span class="sc">%(fathoms)0.2f</span><span class="st"> fathoms)&quot;</span> <span class="op">%</span> {</span>
<span id="cb2-53"><a href="#cb2-53" aria-hidden="true" tabindex="-1"></a>            <span class="st">'miles'</span>: d.mi,</span>
<span id="cb2-54"><a href="#cb2-54" aria-hidden="true" tabindex="-1"></a>            <span class="st">'kilometers'</span>: d.km,</span>
<span id="cb2-55"><a href="#cb2-55" aria-hidden="true" tabindex="-1"></a>            <span class="st">'fathoms'</span>: d.fathom</span>
<span id="cb2-56"><a href="#cb2-56" aria-hidden="true" tabindex="-1"></a>    }</span></code></pre></div>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> <span class="im">import</span> app.models</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> app.models.example()</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>Washington, D.C. <span class="op">-&gt;</span> Chicago <span class="op">-&gt;</span> San Francisco:</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="fl">2458.41</span> miles, <span class="kw">or</span> <span class="fl">3956.42</span> kilometers (<span class="kw">or</span> <span class="fl">2163398.31</span> fathoms)</span></code></pre></div>
<p>The distance function can either be used on its own or as an instance
method, as in Route.length(). If you have any questions, feel free to
ask in the comments.</p>
<h2 id="additional-notes">Additional Notes</h2>
<h3 id="projected-coordinate-systems">Projected Coordinate Systems</h3>
<p>If your data is concentrated in a small area, then you could use a
<a href="http://docs.djangoproject.com/en/dev/ref/contrib/gis/db-api/#distance-lookups" title="GeoDjango Distance Lookups">projected coordinate system</a> (such as SRID 32140, for South Texas) to
solve the problem.</p>
<h3 id="postgis-geography-field-type">PostGIS Geography Field Type</h3>
<p>Alternatively, you could try storing your data using the <a href="http://postgis.refractions.net/docs/ch04.html#PostGIS_Geography" title="PostGIS Geography Field Type">PostGIS
Geography field type</a>, which performs geodesic calculations instead of
Cartesian calculations. The downside with this approach is that it
prevents you from using many of the GeoDjango functions. For instance,
none of the Spatial Aggregate functions worked, in my testing. Since
this is a relatively new PostGIS feature, I expect it to become more
usable in the future. Once it does, I recommend using that instead, as
it makes more sense to have these kinds of calculations occur seamlessly
at the database level.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>BuddyPress Album Author Template Tags</title>
    <link href="https://zach.se/buddypress-album-author-template-tags/" />
    <id>https://zach.se/buddypress-album-author-template-tags/</id>
    <published>2011-01-24T00:00:00Z</published>
    <updated>2011-01-24T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>BuddyPress Album Author Template Tags - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="How to create custom BuddyPress Album+ loops using template tags.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">BuddyPress Album Author Template Tags</a>
</h1>
<section class="page-contents">
    <article>
        <p>So you’ve set up a <a href="../how-to-display-buddypress-album-images-anywhere/" title="How To Display BuddyPress Album Images Anywhere">BuddyPress Album+ loop</a> to place BuddyPress Album+
images anywhere on your site. You’re using the built-in template tags to
display the images, along with some information about each one. These
built-in template tags are great for most purposes, but if you want to
display information about the image author, you were previously out of
luck.</p>
<p>To fill that void, I’ve written a couple of basic template tags which
retrieve information about the author of the current image in the loop.
Just add these template tags to the <code>functions.php</code> file in your theme
directory, and you will be able to use them right away.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;?php</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> bp_album_get_author_userdata() {</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>        <span class="kw">global</span> <span class="va">$pictures_template</span><span class="ot">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="va">$author_userdata</span> <span class="op">=</span> get_userdata(<span class="va">$pictures_template</span>-&gt;picture-&gt;owner_id)<span class="ot">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="va">$author_userdata</span><span class="ot">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> bp_album_get_the_author() {</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>        <span class="va">$author_userdata</span> <span class="op">=</span> bp_album_get_author_userdata()<span class="ot">;</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="va">$author_userdata</span>-&gt;display_name<span class="ot">;</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="kw">?&gt;</span></span></code></pre></div>
<p>The function <code>bp_album_get_author_userdata()</code> retrieves all of the
author information and returns it as an object. See the function
reference for <code>get_userdata()</code> for more information about how to use
this object.</p>
<p>The second function, <code>bp_album_get_the_author()</code>, works similarly to
<code>get_the_author()</code>. It simply returns the display name of the author of
the current image in the loop.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>Recover Lost Partitions with TestDisk</title>
    <link href="https://zach.se/recover-lost-partitions-with-testdisk/" />
    <id>https://zach.se/recover-lost-partitions-with-testdisk/</id>
    <published>2011-01-20T00:00:00Z</published>
    <updated>2011-01-20T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>Recover Lost Partitions with TestDisk - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="How to recover data from broken hard drives with a program called TestDisk.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">Recover Lost Partitions with TestDisk</a>
</h1>
<section class="page-contents">
    <article>
        <p>I was recently messing around with the <code>dd</code> program to write ISO images
to USB drives, instead of wasting DVDs or CDs. The command to do this is
as follows:</p>
<pre><code># dd if=archlinux-2010.05-netinstall-x86_64.iso of=/dev/sdX</code></pre>
<p>Where <code>sdX</code> is the device name of the disk you wish to write the image
to.</p>
<p>This is alright if you only have two disks in your computer, where you
would almost certainly want to write to <code>/dev/sdb</code>. However, if you have
more than two disks, you need to be absolutely sure that you have
selected the correct device — choose the wrong disk, and its data will
be overwritten by that of the ISO image.</p>
<p>This is precisely what happened to me (twice, in fact!). I thought I was
writing to my flash drive but it turned out that I was actually writing
to my 1TB external backup drive. Luckily I had the presence of mind to
kill the dd process after I realized that the activity light on the
flash drive was not blinking, but the 80MB or so of data that was
written was enough to destroy the
<abbr title="Master Boot Record">MBR</abbr>. That’s a big problem
because the MBR acts like a map telling the computer where data is
located on the drive. Without that map, the drive is useless.</p>
<p>Thankfully, one Christophe Grenier has written a program called
<a href="http://www.cgsecurity.org/wiki/TestDisk">TestDisk</a> which scans the raw data of the hard drive and searches for
lost partitions. I was able to recover the lost partitions on my drive
using this fantastic tool.</p>
<p>It uses a few different methods to recover the lost partitions. First,
it quickly scans through the entire hard drive, looking for lost
partitions. Then, it gives you the option to perform a deeper search,
which inspects the drive more carefully. Finally, it checks if there is
a backup MBR on the device, and if so, uses it to replace the lost MBR.</p>
<p>This final option worked for me. Personally, I think this should be the
first step performed, as it takes several hours to scan the entire drive
twice whereas it presumably takes less than a second to inspect the
backup MBR.</p>
<p>Check out this <a href="http://www.cgsecurity.org/wiki/TestDisk_Step_By_Step">step-by-step guide</a> detailing its use.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>How To Display BuddyPress Album Images Anywhere</title>
    <link href="https://zach.se/how-to-display-buddypress-album-images-anywhere/" />
    <id>https://zach.se/how-to-display-buddypress-album-images-anywhere/</id>
    <published>2010-12-21T00:00:00Z</published>
    <updated>2010-12-21T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>How To Display BuddyPress Album Images Anywhere - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="How to use the BuddyPress Album+ loop to display images anywhere on your WordPress site.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">How To Display BuddyPress Album Images Anywhere</a>
</h1>
<section class="page-contents">
    <article>
        <p>Recently, I created a new landing page for <a href="http://ultralightbackpacking.net" title="Ultralight Backpacking Network">Ultralight Backpacking
Network</a>. It features a grid of images posted by the site’s users.
Ultralight Backpacking Network is using BuddyPress Album+ to manage
users’ photo albums, so I needed to figure out a way to display
BuddyPress Album+ images anywhere on the site. It turns out that
BuddyPress Album+ makes this a relatively easy task. You retrieve the
images using a loop, a process familiar to anyone who has modified or
created a WordPress theme. Here’s an example of a BuddyPress Album+
loop:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;photos&quot;</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;?php</span> bp_album_query_pictures('per_page=5'); <span class="kw">?&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;?php</span> if ( bp_album_has_pictures() ) : <span class="kw">?&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&lt;</span><span class="kw">ul</span><span class="ot"> class</span><span class="op">=</span><span class="st">&quot;thumb&quot;</span><span class="dt">&gt;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="kw">&lt;?php</span> while ( bp_album_has_pictures() ) : bp_album_the_picture(); <span class="kw">?&gt;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>                <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>                        <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;</span><span class="er">&lt;</span><span class="st">?php bp_album_picture_middle_url(); ?&gt;&quot;</span><span class="ot"> title</span><span class="op">=</span><span class="st">&quot;</span><span class="er">&lt;</span><span class="st">?php bp_album_picture_title(); ?&gt;&quot;</span><span class="dt">&gt;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>                                <span class="dt">&lt;</span><span class="kw">img</span><span class="ot"> src</span><span class="op">=</span><span class="st">'</span><span class="er">&lt;</span><span class="st">?php bp_album_picture_thumb_url() ?&gt;'</span><span class="ot"> alt</span><span class="op">=</span><span class="st">&quot;</span><span class="er">&lt;</span><span class="st">?php bp_album_picture_desc(); ?&gt;&quot;</span><span class="ot"> </span><span class="dt">/&gt;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>                        <span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>                <span class="dt">&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>        <span class="kw">&lt;?php</span> endwhile; <span class="kw">?&gt;</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&lt;/</span><span class="kw">ul</span><span class="dt">&gt;</span> </span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;?php</span> endif; <span class="kw">?&gt;</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<p>Notice how similar it is to the WordPress loop. An important thing to
remember is that you must first call the function
<code>bp_album_query_pictures()</code> before you can start the BuddyPress Album+
Loop. This function initializes the Query object used for the loop. The
<code>bp_album_query_pictures()</code> function supports a few different
parameters:</p>
<ul>
<li><strong>owner_id</strong> - This is the numeric ID of the user whose photos you
wish to display.</li>
<li><strong>id</strong> - This is the numeric ID of the photo you wish to display.
Only used if you wish to display a single photo.</li>
<li><strong>privacy</strong> - Retrieve photos based on privacy settings. Can be one
of the following:
<ul>
<li><em>public</em> - photos visible to everyone</li>
<li><em>members</em> - photos visible to members</li>
<li><em>friends</em>- photos visible to friends of the current user</li>
<li><em>private</em>- photos visible to the current user</li>
<li><em>admin</em>- photos visible to the site administrator</li>
</ul></li>
<li><strong>adjacent</strong>- Used to select a photo immediately preceding or
succeeding the photo specified by <strong>id</strong>. Use one of either:
<ul>
<li><em>next</em> - select the next photo</li>
<li><em>prev</em> - select the previous photo</li>
</ul></li>
<li><strong>orderkey</strong>- Specify which field to use to sort the photos. This is
one of the following:
<ul>
<li><em>id</em> - sort by the numeric ID of the photos</li>
<li><em>user_id</em>- sort by the numeric ID of the users who uploaded the
photos</li>
<li><em>status</em>- sort based on the status of the photos</li>
</ul></li>
<li><strong>ordersort</strong>- Specify the order in which to sort the photos. One of
either:
<ul>
<li><em>ASC</em> - sort in ascending order</li>
<li><em>DESC</em> - sort in descending order</li>
</ul></li>
<li><strong>per_page</strong> - The number of photos you would like to display per
page. If you want 24 photos, this is where you would specify that.</li>
<li><strong>offset</strong>- Start selecting photos after a certain offset.</li>
</ul>
<p>Once you’ve started the loop, you have a few different options depending
on how you want to display the data. Here are some of the template tags
you can use:</p>
<ul>
<li><strong>bp_album_picture_title</strong> - Prints the title of the image.</li>
<li><strong>bp_album_picture_desc</strong> - Prints the image description.</li>
<li><strong>bp_album_picture_id</strong> - Prints the numeric ID of the image.</li>
<li><strong>bp_album_picture_url</strong> - Prints the URL to the page where the
image is located.</li>
<li><strong>bp_album_picture_original_url</strong> - Prints the URL to the
original image.</li>
<li><strong>bp_album_picture_middle_url</strong> - Prints the URL to the scaled
image.</li>
<li><strong>bp_album_picture_thumb_url</strong>- Prints the URL to the image
thumbnail.</li>
<li><strong>bp_album_adjacent_links</strong> - Prints links to the preceding and
succeeding albums or images.</li>
</ul>
<p>There are several other template tags you can use – too many to list
here, in fact. For more information, check out
<code>includes/bp-album-templatetags.php</code> in the <code>bp-album</code> folder. If you
want to display information about the album author, you are going to
need a few additional template tags that don’t come with BuddyPress
Album+ by default. See <a href="../buddypress-album-author-template-tags/" title="BuddyPress Album Author Template Tags">this post</a> for more details. That’s enough
information to get you up and running with custom BuddyPress Album+
themes.</p>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>
<entry>
    <title>Munchausen Numbers and How to Find Them</title>
    <link href="https://zach.se/munchausen-numbers-and-how-to-find-them/" />
    <id>https://zach.se/munchausen-numbers-and-how-to-find-them/</id>
    <published>2010-12-14T00:00:00Z</published>
    <updated>2010-12-14T00:00:00Z</updated>
    <summary type="html"><![CDATA[<!doctype html>
<html lang="en-us">
    <head>
        <meta charset="utf-8">
        
        <title>Munchausen Numbers and How to Find Them - Zach Denton</title>
        
        <meta name="author" content="Zach Denton">
        <meta name="description" content="An introduction to Munchausen numbers and how to find them with various programming languages.">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>Zach Denton</title>
        <link rel="stylesheet" href="../css/main.css">
        
        <link rel="stylesheet" href="../katex/katex.min.css">
        
    </head>
    <body>
        <header class="site-header">
            <h1 class="site-title"><a class="site-title-link light" href="../">Zach<br>Denton</a></h1>
            <nav class="site-nav">
                <ul class="site-nav-list light">
                    <li><a class="light" href="../about/">About</a></li>
                    <li><a class="light" href="../archives/">Blog</a></li>
                    <li><a class="light" href="../project-euler-solutions/">Euler</a></li>
                    <li><a class="light" href="https://github.com/zacharydenton">GitHub</a></li>
                </ul>
            </nav>
        </header>
        
        <h1 class="page-title post">
    <a class="page-title-link" href=".">Munchausen Numbers and How to Find Them</a>
</h1>
<section class="page-contents">
    <article>
        <p>There are those who would have you believe that <a href="http://en.wikipedia.org/wiki/Interesting_number_paradox">every number is
interesting</a>. However, there are interesting numbers and then there
are <em>interesting</em> numbers. Today I’m going to talk about numbers in the
latter category.</p>
<p>Munchausen numbers are numbers with the property that the sum of their
digits raised to themselves equals the number itself. I think this is
best explained with an example:</p>
<p><span class="math display">3^3 + 4^4 + 3^3 + 5^5 = 3435</span></p>
<p>I don’t know about you, but I find this extremely interesting! (Come on!
You don’t think that’s interesting?) In any case, I decided to write a
few different programs to find these numbers.</p>
<p>The basic algorithm I came up with is as follows:</p>
<ul>
<li>Separate the number into its constituent digits.</li>
<li>Find the sum of each digit raised to itself.</li>
<li>If this sum equals the original number, then it is a Munchausen
number.</li>
</ul>
<p>That’s fairly self-explanatory, right? Let me show you a few different
ways to do this.</p>
<h3 id="ruby">Ruby</h3>
<p>Ruby was the first “real” programming language I learned. Before Ruby, I
was writing simple programs on my TI-89, but that doesn’t really count.
If you’re wondering, I first learned how to program by reading the
excellent <a href="http://pine.fm/LearnToProgram/" title="Learn to Program"><em>Learn to Program</em></a>, by Chris Pine.</p>
<p>In Ruby, the easiest way to get the digits of a number is simply to
convert the number into a string. Once that is established, one simply
needs to apply the aforementioned algorithm to each number within a
certain limit to find the Munchausen numbers within that limit:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode ruby"><code class="sourceCode ruby"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="cf">#!/usr/bin/env ruby</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co"># munchausen.rb - finds munchausen numbers</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="dv">5000</span><span class="at">.times</span> <span class="cf">do</span> <span class="kw">|</span>i<span class="kw">|</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    digits <span class="kw">=</span> i<span class="at">.to_s</span>()</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    sum <span class="kw">=</span> <span class="dv">0</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    digits<span class="at">.each_char</span> <span class="cf">do</span> <span class="kw">|</span>digit<span class="kw">|</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>        digit <span class="kw">=</span> digit<span class="at">.to_i</span>()</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        sum <span class="kw">+=</span> digit <span class="kw">**</span> digit</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">end</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> sum <span class="kw">==</span> i</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>        <span class="fu">puts</span> i<span class="at">.to_s</span>() <span class="kw">+</span> <span class="st">&quot; (munchausen)&quot;</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">end</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="cf">end</span></span></code></pre></div>
<h3 id="c">C</h3>
<p>C is great if you need to eke out every last drop of performance from
your machine. I wouldn’t think of using it in any other case, though.
That being said, it is much easier than assembly language, but come on
people – this is the 21st century.</p>
<p>I took a slightly different approach here and used modular arithmetic to
extract the digits from the number. To find the least significant digit,
we take the remainder when the number is divided by 10. We do this until
all digits have been extracted. Then we simply apply the algorithm as in
the previous example.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">// calculate munchausen numbers</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;math.h&gt;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> limit <span class="op">=</span> <span class="dv">5000</span><span class="op">;</span> <span class="co">// the upper bound of the search</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">1</span><span class="op">;</span> i <span class="op">&lt;</span> limit<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>        <span class="co">// loop through each digit in i</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>        <span class="co">// e.g. for 1000 we get 0, 0, 0, 1.</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>        <span class="dt">int</span> number <span class="op">=</span> i<span class="op">;</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>        <span class="dt">int</span> sum <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="op">(</span>number <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>            <span class="dt">int</span> digit <span class="op">=</span> number <span class="op">%</span> <span class="dv">10</span><span class="op">;</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>            number <span class="op">=</span> number <span class="op">/</span> <span class="dv">10</span><span class="op">;</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>            <span class="co">// find the sum of the digits </span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>            <span class="co">// raised to themselves </span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>            sum <span class="op">+=</span> pow<span class="op">(</span>digit<span class="op">,</span> digit<span class="op">);</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="op">(</span>sum <span class="op">==</span> i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>            <span class="co">// the sum is equal to the number</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>            <span class="co">// itself; thus it is a </span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a>            <span class="co">// munchausen number</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>            printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%i</span><span class="st"> (munchausen)</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> i<span class="op">);</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span> </span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h3 id="clojure">Clojure</h3>
<p>Clojure is an interesting new language, conceived in 2008. It is a Lisp
which targets the Java Virtual Machine, meaning it can make use of any
existing Java code whilst being written in a functional style.</p>
<p>Functional programming is an intriguing concept. It seems more
theoretical than the imperative style I am used to, but perhaps that is
because I am learning the language by reading <a href="http://mitpress.mit.edu/sicp/full-text/book/book.html" title="Structure and Interpretation of Computer Programs"><em>Structure and
Interpretation of Computer Programs</em></a>.</p>
<p>One thing about Clojure that seems strange to me is the fact that it
lacks a standard library. This means that you have to define your own
exponentiation function. In this case, I just used a function from the
Java standard library.</p>
<p>The other idiosyncrasy I noticed is that in Clojure, converting a char
to an int returns the ASCII representation of that char, rather than
performing a direct conversion. Thus, we must subtract 48 in order to
receive the number itself.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>#!/usr/bin/env clojure</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> ** </span>[x n]</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  (. (. java.math.BigInteger (valueOf x)) (pow n)))</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> raise-to-itself </span>[number]</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  (** number number))</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> digits </span>[number]</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">; convert the number to a string,</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  <span class="co">; and convert each char to an int.</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  <span class="co">;</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>  <span class="co">; subtract 48 because casting a char</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>  <span class="co">; to an int returns the ASCII</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>  <span class="co">; representation of that char.</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">map</span> #(<span class="kw">-</span> (<span class="kw">int</span> <span class="va">%</span>) <span class="dv">48</span>) (<span class="kw">str</span> number))) </span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> munchausen? </span>[number]</span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>  <span class="co">; if the sum of the digits raised to</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>  <span class="co">; themselves is equal to the number </span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a>  <span class="co">; itself, then it is a munchausen number.</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">=</span> (<span class="kw">apply</span> <span class="kw">+</span> (<span class="kw">map</span> raise-to-itself (digits number))) number))</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> munchausen </span>(<span class="kw">filter</span> munchausen? (<span class="kw">range</span> <span class="dv">5000</span>)))</span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a>(<span class="kw">println</span> munchausen)</span></code></pre></div>
<h3 id="python">Python</h3>
<p>Python is easily my favorite language. Everything about it just seems
“right”. Of course, this is probably because it is the language I use
the most – but then we have a chicken-or-egg scenario, don’t we?</p>
<p>Here’s the program written in Python. I used it to find every Munchausen
number less than 500,000,000. After thirty minutes or so of intense
computation, it turns out that the only Munchausen numbers are 1 and
3435. Others have posited that 438,579,088 is a Munchausen number, but
this is false because <span class="math inline">0^0 = 1</span>, at least in most programming languages.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="co"># calculates munchausen numbers</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="co"># these are numbers with the property</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="co"># that the sum of its digits raised</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="co"># to themselves produces the original</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="co"># number.</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">5000</span>):</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    digits <span class="op">=</span> (<span class="bu">int</span>(digit) <span class="cf">for</span> digit <span class="kw">in</span> <span class="bu">str</span>(i))</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="bu">sum</span>(digit <span class="op">**</span> digit <span class="cf">for</span> digit <span class="kw">in</span> digits) <span class="op">==</span> i:</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(i, <span class="st">&quot;(munchausen)&quot;</span>)</span></code></pre></div>
    </article>
</section>

        <footer class="site-footer">
            <div class="site-footer-contents">
                <h1 class="site-title"><a class="site-title-link" href="../">Zach<br>Denton</a></h1>
                <nav class="site-nav">
                    <ul class="site-nav-list">
                        <li><a href="../about/">About</a></li>
                        <li><a href="../archives/">Blog</a></li>
                        <li><a href="../project-euler-solutions/">Euler</a></li>
                        <li><a href="https://github.com/zacharydenton">GitHub</a></li>
                    </ul>
                </nav>
            </div>
        </footer>
        
    </body>
</html>
]]></summary>
</entry>

</feed>
